Skip to content

Commit dce1935

Browse files
committed
feat: handler list styling
1 parent 93d28de commit dce1935

File tree

8 files changed

+194
-8
lines changed

8 files changed

+194
-8
lines changed

example/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!doctype html>
1+
<!DOCTYPE html>
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />

example/src/mocks/handlers.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1-
import { http, HttpResponse } from "msw";
1+
import { http, HttpResponse } from "msw"
22

33
export const handlers = [
44
http.get("https://api.example.com/user", () => {
55
return HttpResponse.json({
66
firstName: "John",
77
lastName: "Maverick",
8-
});
8+
})
99
}),
10-
];
10+
http.post("https://api.example.com/user", () => {
11+
return HttpResponse.json({ success: true })
12+
}),
13+
http.put("https://api.example.com/user/:id", () => {
14+
return HttpResponse.json({ success: true })
15+
}),
16+
http.delete("https://api.example.com/user/:id", () => {
17+
return HttpResponse.json({ success: true })
18+
}),
19+
http.patch("https://api.example.com/user/:id", () => {
20+
return HttpResponse.json({ success: true })
21+
}),
22+
http.options("https://api.example.com/user", () => {
23+
return HttpResponse.json({ success: true })
24+
}),
25+
http.head("https://api.example.com/user", () => {
26+
return HttpResponse.json({ success: true })
27+
}),
28+
]

src/app/MSWDevtools.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import "@/shared/styles/global.css"
2+
import { MSWDevtoolsProps } from "../types"
3+
import { DevtoolsHandlerList } from "../features/DevtoolsHandlerList"
4+
import { FixedLayout } from "../shared/ui/FixedLayout"
5+
import { MswControllerHeader } from "../features/MswControllerHeader"
6+
import { MswDevToolsProvider } from "@/providers/useMswDevtoolsContext"
7+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/shared/ui/tabs"
8+
import { Separator } from "@/shared/ui/separator"
9+
10+
export const MSWDevtools = ({
11+
isEnabled = false,
12+
children,
13+
worker,
14+
apiUrl,
15+
}: MSWDevtoolsProps) => {
16+
if ((isEnabled && !worker) || (isEnabled && worker && !worker)) {
17+
console.warn(
18+
"worker is not defined. Please pass in a worker instance from setupWorker(...handlers)"
19+
)
20+
}
21+
22+
if (!isEnabled || !worker) {
23+
return <>{children}</>
24+
}
25+
26+
return (
27+
<>
28+
<MswDevToolsProvider
29+
isEnabled={isEnabled}
30+
apiUrl={apiUrl}
31+
worker={worker}
32+
>
33+
<FixedLayout>
34+
<MswControllerHeader />
35+
<Separator />
36+
<Tabs defaultValue="handlers">
37+
<TabsList className="w-fit">
38+
<TabsTrigger value="handlers">Handlers</TabsTrigger>
39+
<TabsTrigger value="config">Config</TabsTrigger>
40+
</TabsList>
41+
<TabsContent value="handlers">
42+
<div className="px-[16px]">
43+
<DevtoolsHandlerList />
44+
</div>
45+
</TabsContent>
46+
</Tabs>
47+
</FixedLayout>
48+
</MswDevToolsProvider>
49+
{children}
50+
</>
51+
)
52+
}

src/constants.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const ROUTE_METHODS = [
2+
"GET",
3+
"POST",
4+
"PUT",
5+
"PATCH",
6+
"DELETE",
7+
"HEAD",
8+
"OPTION",
9+
"ALL",
10+
] as const
11+
12+
export const MENU_TABS = ["handlers", "config"] as const
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"use client"
2+
3+
import { match } from "ts-pattern"
4+
import { Methods } from ".."
5+
import { useWorkerValue } from "../providers/useMswDevtoolsContext"
6+
7+
import { cn } from "@/shared/lib/utils"
8+
import { Checkbox } from "@/shared/ui/checkbox"
9+
import { InlineCode } from "@/shared/ui/typography"
10+
import { generatorSerializedRouteHandlers } from "@/shared/utils/generatorSerializedRouteHandlers"
11+
12+
export const DevtoolsHandlerList = () => {
13+
const worker = useWorkerValue()
14+
const handlers = worker?.listHandlers() ?? []
15+
const serializedHandlers = generatorSerializedRouteHandlers(handlers)
16+
17+
return (
18+
<ul className="[&>li]:border-b-[1px] [&>li]:border-solid [&>li]:border-white list-none overflow-y-auto h-[250px] scrollbar-hide">
19+
{serializedHandlers.map((handler) => (
20+
<li key={handler.id} className="px-[6px] py-[12px] flex items-center">
21+
<Checkbox id={handler.id} />
22+
<label htmlFor={handler.id} className="pl-[12px]">
23+
<MethodTag method={handler.method} />
24+
<span className="ml-2 font-semibold">{handler.url}</span>
25+
</label>
26+
</li>
27+
))}
28+
</ul>
29+
)
30+
}
31+
32+
export const MethodTag = ({ method }: { method: Methods }) => {
33+
const className = match(method)
34+
.with("GET", () => "text-red-500")
35+
.with("POST", () => "text-blue-500")
36+
.with("PUT", () => "text-green-500")
37+
.with("DELETE", () => "text-yellow-500")
38+
.with("PATCH", () => "text-purple-500")
39+
.otherwise(() => "text-white")
40+
41+
return (
42+
<div className="inline-flex w-[100px] justify-center">
43+
<InlineCode className={cn("bg-slate-950 w-[80px]", className)}>
44+
{method}
45+
</InlineCode>
46+
</div>
47+
)
48+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"use client"
2+
3+
import {
4+
useWorkerValue,
5+
useIsEnabledValue,
6+
useEnabledUpdate,
7+
} from "@/providers/useMswDevtoolsContext"
8+
import { Label } from "@/shared/ui/label"
9+
import { Switch } from "@/shared/ui/switch"
10+
11+
import { useEffect } from "react"
12+
13+
export const MswControllerHeader = () => {
14+
const worker = useWorkerValue()
15+
const isEnabled = useIsEnabledValue()
16+
const setEnabled = useEnabledUpdate()
17+
18+
useEffect(() => {
19+
if (isEnabled) {
20+
worker?.start()
21+
} else {
22+
worker?.stop()
23+
}
24+
}, [isEnabled])
25+
26+
if (!worker) {
27+
return null
28+
}
29+
30+
return (
31+
<div className="flex items-center px-6 py-4">
32+
<Switch
33+
id="terms"
34+
checked={isEnabled}
35+
onCheckedChange={(check) => {
36+
setEnabled(check)
37+
}}
38+
/>
39+
<Label htmlFor="terms" className="pl-[8px] inline-flex font-semibold">
40+
Enable Mock Service Worker
41+
</Label>
42+
</div>
43+
)
44+
}

src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import "./index.css"
2-
import { MSWDevtools } from "./components/MSWDevtools"
1+
import "./shared/styles/global.css"
2+
import { MSWDevtools } from "./app/MSWDevtools"
33

44
export { MSWDevtools }
55
export * from "./types"
6+
export * from "./constants"

src/types.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SetupWorker } from "msw/lib/browser"
22
import { ComponentPropsWithoutRef } from "react"
3+
import { MENU_TABS, ROUTE_METHODS } from "./constants"
34

45
export interface MSWDevtoolsProps extends ComponentPropsWithoutRef<"div"> {
56
/**
@@ -26,10 +27,20 @@ export interface MSWDevtoolsProps extends ComponentPropsWithoutRef<"div"> {
2627
position?: "top" | "bottom"
2728
}
2829

30+
export type Methods =
31+
| "GET"
32+
| "POST"
33+
| "PUT"
34+
| "DELETE"
35+
| "PATCH"
36+
| "HEAD"
37+
| "OPTIONS"
38+
| "ALL"
39+
2940
export type Setting = "mode" | "delay" | "status" | "isHidden"
3041
export type WorkerStatus = "enabled" | "disabled"
3142
export type WorkerMode = "normal" | "error"
32-
43+
export type Tab = (typeof MENU_TABS)[number]
3344
export type Origin = "msw" | "custom"
3445

3546
export type DevToolsHandler = {
@@ -44,7 +55,7 @@ export type DevToolsHandler = {
4455
export interface DevtoolsRoute {
4556
id: string
4657
url: string
47-
method: string
58+
method: Methods
4859
handlers: DevToolsHandler[]
4960
selectedHandler?: string
5061
}

0 commit comments

Comments
 (0)