Skip to content

Commit 455e2bc

Browse files
open api component improvements
1 parent 80720b7 commit 455e2bc

File tree

5 files changed

+378
-188
lines changed

5 files changed

+378
-188
lines changed

apps/portal/src/app/wallets/get-users/page.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Required headers:
7878

7979
### Example
8080

81-
<OpenApiEndpoint path="/v1/wallets/users" method="GET" />
81+
<OpenApiEndpoint path="/v1/wallets/user" method="GET" />
8282

8383
</TabsContent>
8484

apps/portal/src/components/Document/APIEndpointMeta/ApiEndpoint.tsx

Lines changed: 100 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,20 @@ import { cn } from "../../../lib/utils";
33
import { CodeBlock } from "../Code";
44
import { Details } from "../Details";
55
import { Heading } from "../Heading";
6-
import { Paragraph } from "../Paragraph";
76
import { RequestExample } from "./RequestExample";
87

9-
export type APIParameter =
10-
| {
11-
name: string;
12-
required: false;
13-
description: React.ReactNode;
14-
type?: string;
15-
example?:
16-
| string
17-
| boolean
18-
| number
19-
| object
20-
| Array<string | boolean | number | object>;
21-
}
22-
| {
23-
name: string;
24-
required: true;
25-
example:
26-
| string
27-
| boolean
28-
| number
29-
| object
30-
| Array<string | boolean | number | object>;
31-
description: React.ReactNode;
32-
type?: string;
33-
};
8+
export type APIParameter = {
9+
name: string;
10+
required: boolean;
11+
description: React.ReactNode;
12+
type?: string;
13+
example?:
14+
| string
15+
| boolean
16+
| number
17+
| object
18+
| Array<string | boolean | number | object>;
19+
};
3420

3521
export type ApiEndpointMeta = {
3622
title: string;
@@ -98,33 +84,34 @@ export function ApiEndpoint(props: { metadata: ApiEndpointMeta }) {
9884
endpointUrl={props.metadata.path}
9985
method={props.metadata.method}
10086
/>
101-
</div>
10287

103-
<div className="mt-4">
104-
{request.headers.length > 0 && (
105-
<ParameterSection parameters={request.headers} title="Headers" />
106-
)}
107-
108-
{request.pathParameters.length > 0 && (
109-
<ParameterSection
110-
parameters={request.pathParameters}
111-
title="Path Parameters"
112-
/>
113-
)}
114-
115-
{request.queryParameters.length > 0 && (
116-
<ParameterSection
117-
parameters={request.queryParameters}
118-
title="Query Parameters"
119-
/>
120-
)}
121-
122-
{request.bodyParameters.length > 0 && (
123-
<ParameterSection
124-
parameters={request.bodyParameters}
125-
title="Request Body"
126-
/>
127-
)}
88+
{/* Parameters section inside the card */}
89+
<div className="border-t">
90+
{request.headers.length > 0 && (
91+
<ParameterSection parameters={request.headers} title="Headers" />
92+
)}
93+
94+
{request.pathParameters.length > 0 && (
95+
<ParameterSection
96+
parameters={request.pathParameters}
97+
title="Path Parameters"
98+
/>
99+
)}
100+
101+
{request.queryParameters.length > 0 && (
102+
<ParameterSection
103+
parameters={request.queryParameters}
104+
title="Query Parameters"
105+
/>
106+
)}
107+
108+
{request.bodyParameters.length > 0 && (
109+
<ParameterSection
110+
parameters={request.bodyParameters}
111+
title="Request Body"
112+
/>
113+
)}
114+
</div>
128115
</div>
129116
</div>
130117

@@ -174,84 +161,75 @@ function ParameterSection(props: {
174161
parameters: APIParameter[];
175162
}) {
176163
return (
177-
<div className="mb-5">
178-
<h6 className="my-2 break-words text-foreground text-sm font-semibold">
179-
{props.title}
180-
</h6>
181-
<div className="flex flex-col">
182-
{props.parameters
183-
.sort((a, b) => {
184-
if (a.required === b.required) {
185-
return 0;
186-
}
187-
return a.required ? -1 : 1;
188-
})
189-
.map((param) => (
190-
<ParameterItem key={param.name} param={param} />
191-
))}
192-
</div>
164+
<div className="border-b last:border-b-0">
165+
<Details
166+
summary={
167+
<div className="flex items-center gap-3">
168+
<span className="text-sm font-medium">{props.title}</span>
169+
<span className="text-xs text-muted-foreground bg-muted px-2 py-1 rounded">
170+
{props.parameters.length}
171+
</span>
172+
</div>
173+
}
174+
accordionItemClassName="border-0 my-0"
175+
accordionTriggerClassName="p-4 hover:bg-muted/50 transition-colors"
176+
>
177+
<div className="px-4 pb-4">
178+
{props.parameters
179+
.sort((a, b) => {
180+
if (a.required === b.required) {
181+
return 0;
182+
}
183+
return a.required ? -1 : 1;
184+
})
185+
.map((param) => (
186+
<InlineParameterItem key={param.name} param={param} />
187+
))}
188+
</div>
189+
</Details>
193190
</div>
194191
);
195192
}
196193

197-
function ParameterItem({ param }: { param: APIParameter }) {
194+
function InlineParameterItem({ param }: { param: APIParameter }) {
198195
return (
199-
<Details
200-
accordionItemClassName="my-1"
201-
accordionTriggerClassName="font-mono"
202-
summary={
203-
<div className="flex items-center gap-2">
204-
<span>{param.name}</span>
205-
{param.type && (
206-
<span className="text-xs text-muted-foreground px-1.5 py-0.5 bg-muted rounded">
207-
{param.type}
208-
</span>
209-
)}
210-
</div>
211-
}
212-
tags={param.required ? ["Required"] : []}
213-
>
214-
<div className={"flex flex-col gap-3"}>
215-
{param.description && (
216-
<div>
217-
<h5 className="text-sm font-medium mb-1">Description</h5>
218-
<Paragraph className="text-sm">{param.description}</Paragraph>
219-
</div>
220-
)}
221-
196+
<div className="flex flex-col gap-2 p-3 bg-muted/30 rounded-lg">
197+
<div className="flex items-center gap-2 flex-wrap">
198+
<code className="text-foreground text-sm font-mono bg-background px-2 py-1 rounded border">
199+
{param.name}
200+
</code>
222201
{param.type && (
223-
<div>
224-
<h5 className="text-sm font-medium mb-1">Type</h5>
225-
<div className="rounded-lg border">
226-
<CodeBlock
227-
className="border-none"
228-
code={param.type}
229-
containerClassName="mb-0"
230-
lang="typescript"
231-
/>
232-
</div>
233-
</div>
202+
<span className="text-xs text-muted-foreground px-2 py-1 bg-muted rounded">
203+
{param.type}
204+
</span>
234205
)}
235-
236-
{param.example !== undefined && (
237-
<div>
238-
<h5 className="text-sm font-medium mb-1">Example</h5>
239-
<div className="rounded-lg border">
240-
<CodeBlock
241-
className="border-none"
242-
code={
243-
typeof param.example === "object"
244-
? JSON.stringify(param.example, null, 2)
245-
: String(param.example)
246-
}
247-
containerClassName="mb-0"
248-
lang={typeof param.example === "object" ? "json" : "text"}
249-
/>
250-
</div>
251-
</div>
206+
{param.required && (
207+
<span className="text-xs text-warning-text px-2 py-1 rounded border border-warning-text">
208+
Required
209+
</span>
252210
)}
253211
</div>
254-
</Details>
212+
213+
{param.description && (
214+
<div className="text-sm text-muted-foreground">{param.description}</div>
215+
)}
216+
217+
{param.example !== undefined && (
218+
<div className="text-sm flex flex-col gap-2">
219+
<span className="text-muted-foreground">Example: </span>
220+
<CodeBlock
221+
code={
222+
typeof param.example === "object"
223+
? JSON.stringify(param.example)
224+
: String(param.example)
225+
}
226+
containerClassName="m-0"
227+
lang="json"
228+
scrollContainerClassName="max-h-[200px]"
229+
/>
230+
</div>
231+
)}
232+
</div>
255233
);
256234
}
257235

@@ -335,7 +313,7 @@ function createFetchCommand(params: { metadata: ApiEndpointMeta }) {
335313
}
336314

337315
if (Object.keys(bodyObj).length > 0) {
338-
fetchOptions.body = JSON.stringify(bodyObj);
316+
fetchOptions.body = bodyObj;
339317
}
340318

341319
return `fetch('${url}', ${JSON.stringify(fetchOptions, null, 2)})`;

0 commit comments

Comments
 (0)