Skip to content

Commit

Permalink
Add useParam hook and example Storybook stories
Browse files Browse the repository at this point in the history
  • Loading branch information
ptb committed Aug 1, 2024
1 parent 0f6d4e1 commit 603315d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 0 deletions.
48 changes: 48 additions & 0 deletions web/src/hooks/use-param.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useCallback, useEffect, useState } from "react"

/** Uses the URL search params to store a value in addition to React state. */
export const useParam = <T>(
key: string,
convert: (input: string | T) => T,
defaultValue: T
): [T, (input: T) => void] => {
/** Get value from URL search param or use `defaultValue`. */
const getParam = useCallback(
() =>
convert(
new URLSearchParams(window.location.search).get(key) ??
defaultValue
),
[convert, defaultValue, key]
)

const [value, setValue] = useState(getParam)

/** Set value in URL search param *and* React state. */
const setParam = useCallback(
(input: T) => {
const url = new URL(window.location.href)

url.searchParams.set(key, String(input))
window.history.pushState(null, "", url)

setValue(input)
},
[key]
)

/** Update React state on browser navigation `popstate` events. */
useEffect(() => {
const popParam = () => {
setValue(getParam())
}

window.addEventListener("popstate", popParam)

return () => {
window.removeEventListener("popstate", popParam)
}
}, [getParam])

return [value, setParam]
}
23 changes: 23 additions & 0 deletions web/src/sections/LandingPage/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from "@storybook/react"

import { LandingPage } from "./index"

const meta = {
/** Only required props should be in the default export */
args: {},
component: LandingPage,
tags: ["autodocs"]
// title: "Sections/LandingPage"
} satisfies Meta<typeof LandingPage>

type Story = StoryObj<typeof meta>

export default meta

/**
The default look of the Component, only with simple required props.
We're hiding the panel to allow a full view of the Component.
*/
export const Default: Story = {
args: {}
}

0 comments on commit 603315d

Please sign in to comment.