Skip to content

Commit

Permalink
feat: Scenario toggle add description add
Browse files Browse the repository at this point in the history
Co-authored-by: Minseok Choi <[email protected]>
  • Loading branch information
minsgy and minsgy committed Jun 19, 2024
1 parent 9ca2964 commit 7fb48ff
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 83 deletions.
21 changes: 16 additions & 5 deletions example/src/mocks/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,23 @@ worker.scenario([
{
description: "장바구니 상품 추가 시나리오",
handlers: [
http.get("https://jsonplaceholder.typicode.com/cart", () => {
return HttpResponse.json({
firstName: "111",
lastName: "111",
http
.get("https://jsonplaceholder.typicode.com/cart", () => {
return HttpResponse.json({
firstName: "111",
lastName: "111",
})
})
}),
.presets([
{
status: 200,
description: "장바구니 상품 추가",
response: {
firstName: "111",
lastName: "111",
},
},
]),
http.post("https://jsonplaceholder.typicode.com/cart/1", () => {
return HttpResponse.json({
firstName: "222",
Expand Down
2 changes: 0 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { HttpMethods } from "msw"

export const ROUTE_METHODS = Object.values(HttpMethods)

export const MENU_TABS = ["handlers", "config"] as const
2 changes: 1 addition & 1 deletion src/features/MSWDevtoolsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const MSWDevtoolsPanel = ({
</Button>
</div>
<Separator />
<TabsContent value="routes">
<TabsContent value="routes" className="flex-1 min-h-0">
{isOpenEditorPanel && <RouteEditPanel />}
<MswControllerHeader />
<Separator />
Expand Down
144 changes: 90 additions & 54 deletions src/features/ScenarioPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,117 @@
import {
useIsEnabledValue,
useScenarioState,
useWorkerValue,
} from "@/providers/useMswDevtoolsContext"
import { Separator } from "@/shared/ui/separator"
import { H2, InlineCode, P } from "@/shared/ui/typography"
import { InlineCode, P } from "@/shared/ui/typography"
import { generatorSerializedRouteHandlers } from "@/shared/utils/generatorSerializedRouteHandlers"
import { TableRow, TableBody, TableCell, Table } from "@/shared/ui/table"
import { MethodTag } from "../DevtoolsHandlerList"

import { Switch } from "@/shared/ui/switch"
import { Fragment } from "react"
import { HandlerSelect } from "../HandlerSelect"
import { Toggle } from "@/shared/ui/toggle"
import { Check } from "@/shared/icons"
import { ScenarioRoutePreset } from "@/types"
export const ScenarioPanel = () => {
const isEnabled = useIsEnabledValue()
const { listScenarios } = useWorkerValue()
const { selectedScenario, onSelectScenario } = useScenarioState()

const handleToggleScenario = (
scenario: ScenarioRoutePreset,
isPressed: boolean
) => {
if (!isPressed) {
onSelectScenario(null)
return
}

onSelectScenario(scenario)
}

return (
<div className="flex flex-col w-full h-full">
<header className="flex justify-between align-items px-[16px] py-[12px]">
<H2>Scenario Case</H2>
<P>
<InlineCode>Scenario</InlineCode> allows you to manage mocking by
presets a single case with multiple APIs.
</P>
</header>
<Separator />
<div className="overflow-y-auto scrollbar-hide">
{listScenarios().map((scenario) => (
<Fragment key={scenario.id}>
<div className="px-4 py-4 bg-secondary">
<P>
<InlineCode>Description</InlineCode> {scenario.description}
</P>
</div>
<Table>
<TableBody>
{generatorSerializedRouteHandlers(scenario.handlers).map(
(route) => (
<TableRow key={route.id} className="py-[16px]">
<TableCell>
<MethodTag method={route.method} />
</TableCell>
<TableCell>
<span className="flex-1 pl-[12px] font-semibold text-left break-word inline-block">
{route.url}
</span>
</TableCell>
<TableCell>
<div className="flex items-center">
<HandlerSelect
value={route.selectedHandlerId}
onValueChange={(handlerId) => {
console.log(handlerId, route.id)
// onSelectHandler(route.id, handlerId)
}}
options={route.handlers}
defaultValue={
route.selectedHandlerId ?? route.handlers[0].id
}
/>
{route.origin === "custom" && (
<InlineCode>{route.origin}</InlineCode>
)}
<div className="flex ml-auto items-center">
<Switch
disabled={!isEnabled}
checked={route.enabled}
// onCheckedChange={(checked) => {
// onToggleHandler(route.id, checked)
// }}
{listScenarios.map((scenario) => {
const isSelected = selectedScenario?.id === scenario.id
return (
<Fragment key={scenario.id}>
<div className="px-4 py-4 bg-secondary flex items-center justify-between">
<div className="flex items-center space-x-2">
<InlineCode className="border-b-gray-500">
Description
</InlineCode>
<P>{scenario.description}</P>
</div>
<Toggle
value={scenario.id}
aria-label="scenario toggle"
pressed={isSelected}
onPressedChange={(pressed) =>
handleToggleScenario(scenario, pressed)
}>
{isSelected && <Check />}
{isSelected ? "Selected" : "Select"}
</Toggle>
</div>
<Table>
<TableBody>
{generatorSerializedRouteHandlers(scenario.handlers).map(
(route) => (
<TableRow key={route.id} className="py-[16px]">
<TableCell width={100}>
<MethodTag method={route.method} />
</TableCell>
<TableCell>
<span className="flex-1 pl-[12px] font-semibold text-left break-word inline-block">
{route.url}
</span>
</TableCell>
<TableCell>
<div className="flex items-center">
<HandlerSelect
value={route.selectedHandlerId}
onValueChange={(handlerId) => {
console.log(handlerId, route.id)
// onSelectHandler(route.id, handlerId)
}}
options={route.handlers}
defaultValue={
route.selectedHandlerId ?? route.handlers[0].id
}
/>
{route.origin === "custom" && (
<InlineCode>{route.origin}</InlineCode>
)}
<div className="flex ml-auto items-center">
<Switch
disabled={!isEnabled}
checked={route.enabled}
// onCheckedChange={(checked) => {
// onToggleHandler(route.id, checked)
// }}
/>
</div>
</div>
</div>
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Fragment>
))}
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Fragment>
)
})}
</div>
</div>
)
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/useMswDevtoolsState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const useMswDevtoolsState = ({
generatorSerializedRouteHandlers(worker?.listHandlers() ?? [])
)
const [selectedScenario, setSelectedScenario] =
useState<ScenarioRoutePreset | null>(worker?.listScenarios()[0] ?? null)
useState<ScenarioRoutePreset | null>(null)

const onDeleteHandler = (id: string) => {
setRoutes((route) => route.filter((route) => route.id !== id))
Expand Down Expand Up @@ -48,14 +48,13 @@ export const useMswDevtoolsState = ({
setRoutes(newRoutes)
}

const onSelectScenario = (scenario: ScenarioRoutePreset) => {
const onSelectScenario = (scenario: ScenarioRoutePreset | null) => {
setSelectedScenario(scenario)
}

useEffect(
function setupHandlers() {
if (worker) {
console.log(worker.listHandlers())
const httpUsedRoutes = generatorRequestHandler(routes)
const selectedScenarioHandlers = selectedScenario?.handlers ?? []
worker.resetHandlers(...httpUsedRoutes, ...selectedScenarioHandlers)
Expand All @@ -66,7 +65,7 @@ export const useMswDevtoolsState = ({
}
}
},
[routes, worker, onRouteUpdate]
[routes, worker, onRouteUpdate, selectedScenario]
)

useEffect(
Expand Down
2 changes: 2 additions & 0 deletions src/providers/useMswDevtoolsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const [
useEnabledUpdate,
useRoute,
useEditorRouteState,
useScenarioState,
] = constate(
(initialState: MswDevtoolsContextType) => {
const {
Expand Down Expand Up @@ -95,4 +96,5 @@ export {
useEnabledUpdate,
useRoute,
useEditorRouteState,
useScenarioState,
}
17 changes: 17 additions & 0 deletions src/shared/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,20 @@ export const Edit = () => {
</svg>
)
}

export const Check = () => {
return (
<svg
width="24"
height="24"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"></path>
</svg>
)
}
12 changes: 9 additions & 3 deletions src/shared/lib/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,27 @@ const createProxyMethod = <
const [url, responseHandlers] = argArray
const responseHandlersWithPresets = (...rest: any) =>
responseHandlers(...rest)
const response: HttpResponse = responseHandlers()
const result: HttpHandler = Reflect.apply(target, thisArg, [
url,
responseHandlersWithPresets,
[
{
status: response.status,
description: response.statusText,
response: response, // TODO: response.json() async
},
],
])

const response: HttpResponse = responseHandlers()

function presets(presets: HttpPreset[]) {
const defaultResponseWithPresets: HttpPreset[] = [
...presets,
{
status: response.status,
description: response.statusText,
response: response, // TODO: response.json() async
},
...presets,
]
return Reflect.apply(target, thisArg, [
url,
Expand Down
7 changes: 2 additions & 5 deletions src/shared/lib/worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type SetupWorker = (
*
*/
scenario: (scenarioRoutePreset: ScenarioRoutePreset[]) => void
listScenarios: () => ScenarioRoutePreset[]
listScenarios: ScenarioRoutePreset[]
}

const setupWorker = new Proxy(originSetupWorker, {
Expand All @@ -26,13 +26,10 @@ const setupWorker = new Proxy(originSetupWorker, {
id: id ?? createdUuid(),
...scenario,
}))

scenarios.push(...newScenario)
return newScenario
}
originWorker.listScenarios = () => {
return scenarios
}
originWorker.listScenarios = scenarios
return originWorker
},
}) as SetupWorker
Expand Down
5 changes: 1 addition & 4 deletions src/shared/ui/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,7 @@ const TableCell = React.forwardRef<
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
"py-4 px-6 align-middle [&:has([role=checkbox])]:pr-0",
className
)}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
Expand Down
4 changes: 2 additions & 2 deletions src/shared/ui/toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/shared/lib/cn"

const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-primary data-[state=on]:text-accent-foreground",
{
variants: {
variant: {
default: "bg-transparent",
default: "bg-popover text-foreground",
outline:
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
},
Expand Down
5 changes: 2 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { HttpMethods } from "msw"
import { HttpHandler, HttpMethods } from "msw"
import { SetupWorker } from "./shared/lib/worker"
import { Http } from "./shared/lib/http"

export interface MSWDevtoolsProps {
/**
Expand Down Expand Up @@ -55,7 +54,7 @@ export interface MSWDevtoolsProps {
export type ScenarioRoutePreset = {
id?: string
description: string
handlers: ReturnType<Http[keyof Http]>[]
handlers: HttpHandler[]
}

export type Position = "top-left" | "top-right" | "bottom-left" | "bottom-right"
Expand Down

0 comments on commit 7fb48ff

Please sign in to comment.