Skip to content

Commit

Permalink
refactor: floating button ui refactor with panel styles
Browse files Browse the repository at this point in the history
  • Loading branch information
minsgy committed Jun 10, 2024
1 parent d4922c2 commit 0d3b91d
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 82 deletions.
7 changes: 5 additions & 2 deletions src/app/MSWDevtools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ export const MSWDevtools = ({
children,
worker,
onRouteUpdate,
position,
initialOpen = false,
}: MSWDevtoolsProps) => {
if ((isEnabled && !worker) || (isEnabled && worker && !worker)) {
if (isEnabled && !worker) {
console.warn(
"worker is not defined. Please pass in a worker instance from setupWorker(...handlers)"
)
Expand All @@ -22,11 +24,12 @@ export const MSWDevtools = ({
return (
<>
<MswDevToolsProvider
initialOpen={initialOpen}
isEnabled={isEnabled}
worker={worker}
onRouteUpdate={onRouteUpdate}
>
<MSWDevtoolsPanel />
<MSWDevtoolsPanel position={position} />
</MswDevToolsProvider>
{children}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { MswLogo } from "@/shared/icons"
import { cn } from "@/shared/lib/cn"
import { Button } from "@/shared/ui/button"
import { ButtonHTMLAttributes } from "react"
import { match } from "ts-pattern"
import { Position } from ".."

type DevtoolsButtonProps = {
position: "top-left" | "top-right" | "bottom-left" | "bottom-right"
type DevtoolsFloatingButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
position: Position
}

export const DevtoolsButton = ({
export const DevtoolsFloatingButton = ({
position = "bottom-right",
}: DevtoolsButtonProps) => {
...rest
}: DevtoolsFloatingButtonProps) => {
const positionClassName = match(position)
.with("top-left", () => "top-[16px] left-[16px]")
.with("top-right", () => "top-[16px] right-[16px]")
Expand All @@ -20,14 +23,15 @@ export const DevtoolsButton = ({
return (
<div
className={cn(
"z-[100000] fixed p-[4px] text-left flex items-center justify-center shadow-md rounded-full",
"z-[100000] fixed p-[4px] text-left flex items-center justify-center rounded-full ",
positionClassName
)}
>
{/* TODO: LOGO CHANGE (temporary logo) */}
<Button
className="[&>svg]:absolute [&>svg]:w-[90%] w-[64px] h-[64px] shadow-inner"
className="[&>svg]:absolute w-[64px] h-[64px] shadow-md shadow-black transition-transform duration-200 ease-in-out hover:scale-[1.02]"
variant="destructive"
{...rest}
>
<MswLogo />
</Button>
Expand Down
3 changes: 3 additions & 0 deletions src/features/DevtoolsHandlerList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { InlineCode, P } from "@/shared/ui/typography"
import { HttpMethods } from "msw"
import {
useEditorRouteState,
useIsEnabledValue,
useRoute,
} from "@/providers/useMswDevtoolsContext"
import { HandlerSelect } from "./HandlerSelect"
Expand All @@ -15,6 +16,7 @@ import { Switch } from "@/shared/ui/switch"
import { Input } from "@/shared/ui/input"

export const DevtoolsHandlerList = () => {
const isEnabled = useIsEnabledValue()
const { routes, onToggleHandler, onSelectHandler } = useRoute()
const { onOpenEditPanel } = useEditorRouteState()

Expand Down Expand Up @@ -56,6 +58,7 @@ export const DevtoolsHandlerList = () => {
Edit
</Button>
<Switch
disabled={!isEnabled}
checked={route.enabled}
onCheckedChange={(checked) => {
onToggleHandler(route.id, checked)
Expand Down
53 changes: 36 additions & 17 deletions src/features/MSWDevtoolsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,47 @@ import { RouteEditPanel } from "./RouteEditPanel"
import { Separator } from "@/shared/ui/separator"
import { DevtoolsHandlerList } from "./DevtoolsHandlerList"
import { MswControllerHeader } from "./MswControllerHeader"
import { useEditorRouteState } from "@/providers/useMswDevtoolsContext"
import { DevtoolsButton } from "./DevtoolsButton"
import {
useEditorRouteState,
useFloatingState,
} from "@/providers/useMswDevtoolsContext"
import { DevtoolsFloatingButton } from "./DevtoolsFloatingButton"
import { Position } from ".."

export const MSWDevtoolsPanel = () => {
type MSWDevtoolsPanelProps = {
position?: Position
}

export const MSWDevtoolsPanel = ({
position = "bottom-right",
}: MSWDevtoolsPanelProps) => {
const { isOpenEditorPanel } = useEditorRouteState()
const { isFloatingOpen, setIsFloatingOpen } = useFloatingState()

return (
<>
<DevtoolsButton position="bottom-right" />
<FixedLayout>
{isOpenEditorPanel ? (
<RouteEditPanel />
) : (
<>
<MswControllerHeader />
<Separator />
<div className="p-[16px] h-full">
<DevtoolsHandlerList />
</div>
</>
)}
</FixedLayout>
{isFloatingOpen ? (
<FixedLayout>
{isOpenEditorPanel ? (
<RouteEditPanel />
) : (
<>
<MswControllerHeader />
<Separator />
<div className="p-[16px] h-full">
<DevtoolsHandlerList />
</div>
</>
)}
</FixedLayout>
) : (
<DevtoolsFloatingButton
position={position}
onClick={() => {
setIsFloatingOpen(true)
}}
/>
)}
</>
)
}
45 changes: 22 additions & 23 deletions src/features/MswControllerHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,18 @@ import {
useIsEnabledValue,
useEnabledUpdate,
useEditorRouteState,
useFloatingState,
} from "@/providers/useMswDevtoolsContext"
import { Close } from "@/shared/icons"
import { Button } from "@/shared/ui/button"
import { Label } from "@/shared/ui/label"
import { Switch } from "@/shared/ui/switch"

import { useEffect } from "react"

export const MswControllerHeader = () => {
const worker = useWorkerValue()
const isEnabled = useIsEnabledValue()
const setEnabled = useEnabledUpdate()
const { setIsOpenEditorPanel } = useEditorRouteState()

useEffect(() => {
if (isEnabled) {
worker?.start()
} else {
worker?.stop()
}
}, [isEnabled])

if (!worker) {
return null
}
const { setIsFloatingOpen } = useFloatingState()

return (
<div className="flex items-center px-6 py-4 justify-between">
Expand All @@ -44,14 +32,25 @@ export const MswControllerHeader = () => {
Enable Mock Service Worker
</Label>
</div>
<Button
variant="outline"
onClick={() => {
setIsOpenEditorPanel(true)
}}
>
Add Route
</Button>
<div className="flex">
<Button
variant="outline"
onClick={() => {
setIsOpenEditorPanel(true)
}}
>
Add Route
</Button>
<Button
variant="secondary"
className="ml-[4px]"
onClick={() => {
setIsFloatingOpen(false)
}}
>
<Close />
</Button>
</div>
</div>
)
}
59 changes: 36 additions & 23 deletions src/hooks/useMswDevtoolsState.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
"use client"

import { useState, useCallback, useEffect, useRef } from "react"
import { useState, useEffect, useRef } from "react"
import { MswDevtoolsContextType } from "../providers/useMswDevtoolsContext"
import { EnhancedDevtoolsRoute } from ".."
import { generatorSerializedRouteHandlers } from "@/shared/utils/generatorSerializedRouteHandlers"
import { generatorRequestHandler } from "@/shared/utils/generatorRequestHandler"

export const useMswDevtoolsState = ({
onRouteUpdate,
...initialState
initialOpen,
isEnabled: initialIsEnabled,
worker,
}: MswDevtoolsContextType) => {
const isMounted = useRef(false)
const [state, setState] = useState(initialState)
const [isEnabled, setIsEnabled] = useState(initialIsEnabled ?? true)
const [isFloatingOpen, setIsFloatingOpen] = useState(initialOpen ?? false)
const [routes, setRoutes] = useState<EnhancedDevtoolsRoute[]>(
generatorSerializedRouteHandlers(initialState.worker?.listHandlers() ?? [])
generatorSerializedRouteHandlers(worker?.listHandlers() ?? [])
)

const setEnabled = useCallback((isEnabled: boolean) => {
setState((prev) => ({
...prev,
isEnabled,
}))
}, [])

const onDeleteHandler = (id: string) => {
setRoutes((route) => route.filter((route) => route.id !== id))
}
Expand All @@ -45,27 +41,44 @@ export const useMswDevtoolsState = ({
setRoutes(newRoutes)
}

useEffect(() => {
if (state.worker) {
const httpUsedRoutes = generatorRequestHandler(routes)
state.worker.resetHandlers(...httpUsedRoutes)
// first call is not needed
if (isMounted.current) {
onRouteUpdate?.(routes)
useEffect(
function setupHandlers() {
if (worker) {
const httpUsedRoutes = generatorRequestHandler(routes)
worker.resetHandlers(...httpUsedRoutes)
// first call is not needed
if (isMounted.current) {
onRouteUpdate?.(routes)
} else {
isMounted.current = true
}
}
},
[routes, worker]
)

useEffect(
function setupWorkerEnabled() {
if (isEnabled) {
worker.start()
} else {
isMounted.current = true
worker.stop()
}
}
}, [routes, state.worker])
},
[isEnabled]
)

return {
state,
setEnabled,
worker,
isEnabled,
setIsEnabled,
routes,
setRoutes,
onDeleteHandler,
onAddHandler,
onToggleHandler,
onSelectHandler,
isFloatingOpen,
setIsFloatingOpen,
}
}
26 changes: 19 additions & 7 deletions src/providers/useMswDevtoolsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,24 @@ const [
MswDevToolsProvider,
useIsEnabledValue,
useWorkerValue,
useFloatingState,
useEnabledUpdate,
useRoute,
useEditorRouteState,
] = constate(
(initialState: MswDevtoolsContextType) => {
const {
state,
setEnabled,
worker,
setIsEnabled,
routes,
setRoutes,
onAddHandler,
onDeleteHandler,
onToggleHandler,
onSelectHandler,
isFloatingOpen,
isEnabled,
setIsFloatingOpen,
} = useMswDevtoolsState(initialState)
const {
selectedRoute,
Expand All @@ -38,8 +42,8 @@ const [
} = useEditorPanelState()

return {
state,
setEnabled,
worker,
setIsEnabled,
routes,
setRoutes,
onAddHandler,
Expand All @@ -51,11 +55,18 @@ const [
isOpenEditorPanel,
onCloseEditPanel,
onSelectHandler,
isFloatingOpen,
setIsFloatingOpen,
isEnabled,
}
},
(value) => value.state.isEnabled,
(value) => value.state.worker,
(value) => value.setEnabled,
(value) => value.isEnabled,
(value) => value.worker,
(value) => ({
isFloatingOpen: value.isFloatingOpen,
setIsFloatingOpen: value.setIsFloatingOpen,
}),
(value) => value.setIsEnabled,
(value) => ({
routes: value.routes,
onRoutesChange: value.setRoutes,
Expand All @@ -80,4 +91,5 @@ export {
useEnabledUpdate,
useRoute,
useEditorRouteState,
useFloatingState,
}
Loading

0 comments on commit 0d3b91d

Please sign in to comment.