mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
OpenAPI: improve UX (#2656)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-openapi': patch
|
||||
---
|
||||
|
||||
Improve UX
|
||||
@@ -51,7 +51,7 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from 'fumadocs-ui/components/ui/collapsible';
|
||||
import { ChevronDown, LoaderCircle } from 'lucide-react';
|
||||
import { X, ChevronDown, LoaderCircle } from 'lucide-react';
|
||||
import { encodeRequestData } from '@/requests/media/encode';
|
||||
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
|
||||
import { cn } from 'fumadocs-ui/utils/cn';
|
||||
@@ -351,7 +351,9 @@ export default function PlaygroundClient({
|
||||
</SecurityTabs>
|
||||
)}
|
||||
<FormBody body={body} parameters={parameters} />
|
||||
{testQuery.data ? <ResultDisplay data={testQuery.data} /> : null}
|
||||
{testQuery.data ? (
|
||||
<ResultDisplay data={testQuery.data} reset={testQuery.reset} />
|
||||
) : null}
|
||||
</form>
|
||||
</SchemaProvider>
|
||||
</FormProvider>
|
||||
@@ -764,15 +766,34 @@ function Route({
|
||||
);
|
||||
}
|
||||
|
||||
function DefaultResultDisplay({ data }: { data: FetchResult }) {
|
||||
function DefaultResultDisplay({
|
||||
data,
|
||||
reset,
|
||||
}: {
|
||||
data: FetchResult;
|
||||
reset: () => void;
|
||||
}) {
|
||||
const statusInfo = useMemo(() => getStatusInfo(data.status), [data.status]);
|
||||
const { shikiOptions } = useApiContext();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 p-3">
|
||||
<div className="inline-flex items-center gap-1.5 text-sm font-medium text-fd-foreground">
|
||||
<statusInfo.icon className={cn('size-4', statusInfo.color)} />
|
||||
{statusInfo.description}
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="inline-flex items-center gap-1.5 text-sm font-medium text-fd-foreground">
|
||||
<statusInfo.icon className={cn('size-4', statusInfo.color)} />
|
||||
{statusInfo.description}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
buttonVariants({ size: 'icon-xs' }),
|
||||
'p-0 text-fd-muted-foreground hover:text-fd-accent-foreground [&_svg]:size-3.5',
|
||||
)}
|
||||
onClick={() => reset()}
|
||||
aria-label="Dismiss response"
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm text-fd-muted-foreground">{data.status}</p>
|
||||
{data.data !== undefined && (
|
||||
|
||||
@@ -408,7 +408,11 @@ export function FieldSet({
|
||||
}}
|
||||
>
|
||||
{union.map((item, i) => (
|
||||
<option key={i} value={i}>
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
className="bg-fd-popover text-fd-popover-foreground"
|
||||
>
|
||||
{schemaToString(item, undefined, FormatFlags.UseAlias)}
|
||||
</option>
|
||||
))}
|
||||
@@ -449,7 +453,11 @@ export function FieldSet({
|
||||
}}
|
||||
>
|
||||
{field.type.map((item) => (
|
||||
<option key={item} value={item}>
|
||||
<option
|
||||
key={item}
|
||||
value={item}
|
||||
className="bg-fd-popover text-fd-popover-foreground"
|
||||
>
|
||||
{item}
|
||||
</option>
|
||||
))}
|
||||
|
||||
@@ -90,10 +90,21 @@ export async function Operation({
|
||||
<SelectTabs defaultValue={defaultValue}>
|
||||
<div className="flex gap-2 items-end justify-between">
|
||||
{ctx.renderHeading(headingLevel, 'Request Body')}
|
||||
<SelectTabTrigger
|
||||
items={contentTypes.map(([key]) => key)}
|
||||
className="mb-4"
|
||||
/>
|
||||
{contentTypes.length > 1 ? (
|
||||
<SelectTabTrigger
|
||||
items={contentTypes.map(([key]) => key)}
|
||||
className="mb-4"
|
||||
/>
|
||||
) : (
|
||||
<p
|
||||
className={cn(
|
||||
'text-sm text-fd-muted-foreground font-medium not-prose',
|
||||
headingLevel === 2 ? 'mb-7' : 'mb-4',
|
||||
)}
|
||||
>
|
||||
{defaultValue}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{body.description && ctx.renderMarkdown(body.description)}
|
||||
{contentTypes.map(([type, content]) => {
|
||||
@@ -186,7 +197,13 @@ export async function Operation({
|
||||
<SelectTabs defaultValue={names[0]}>
|
||||
<div className="flex items-end justify-between gap-2">
|
||||
{ctx.renderHeading(headingLevel, 'Authorization')}
|
||||
<SelectTabTrigger items={names} className="mb-4" />
|
||||
{names.length > 1 ? (
|
||||
<SelectTabTrigger items={names} className="mb-4" />
|
||||
) : (
|
||||
<p className="text-sm text-fd-muted-foreground font-medium not-prose mb-7">
|
||||
{names[0]}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{securities.map((security, i) => (
|
||||
<SelectTab key={i} value={names[i]}>
|
||||
@@ -217,10 +234,16 @@ export async function Operation({
|
||||
<SelectTabs defaultValue={defaultValue}>
|
||||
<div className="flex justify-between gap-2 items-end">
|
||||
{ctx.renderHeading(headingLevel, 'Callbacks')}
|
||||
<SelectTabTrigger
|
||||
items={callbacks.map(([key]) => key)}
|
||||
className="mb-4"
|
||||
/>
|
||||
{callbacks.length > 1 ? (
|
||||
<SelectTabTrigger
|
||||
items={callbacks.map(([key]) => key)}
|
||||
className="mb-4"
|
||||
/>
|
||||
) : (
|
||||
<p className="text-sm text-fd-muted-foreground font-medium not-prose mb-7">
|
||||
{defaultValue}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{callbacks.map(([name, callback]) => (
|
||||
<SelectTab key={name} value={name}>
|
||||
@@ -356,7 +379,7 @@ async function ResponseAccordion({
|
||||
</AccordionHeader>
|
||||
<AccordionContent className="ps-4.5">
|
||||
{response.description && (
|
||||
<div className="prose-no-margin">
|
||||
<div className="prose-no-margin mb-2">
|
||||
{ctx.renderMarkdown(response.description)}
|
||||
</div>
|
||||
)}
|
||||
@@ -371,7 +394,7 @@ async function ResponseAccordion({
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectTab key={type} value={type} className="my-2">
|
||||
<SelectTab key={type} value={type} className="mb-2">
|
||||
{ts && <CopyResponseTypeScript code={ts} />}
|
||||
{schema && (
|
||||
<div className="border px-3 py-2 rounded-lg">
|
||||
|
||||
@@ -4,6 +4,7 @@ export function useQuery<I extends unknown[], T>(
|
||||
fn: (...input: I) => Promise<T>,
|
||||
): {
|
||||
start: (...input: I) => void;
|
||||
reset: () => void;
|
||||
data?: T;
|
||||
error?: unknown;
|
||||
isLoading: boolean;
|
||||
@@ -36,6 +37,11 @@ export function useQuery<I extends unknown[], T>(
|
||||
setLoading(false);
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
setData(undefined);
|
||||
setError(undefined);
|
||||
setLoading(false);
|
||||
},
|
||||
}),
|
||||
[error, data, loading],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user