Skip to content

Commit

Permalink
feat(router): update router to support query params in intent links (#…
Browse files Browse the repository at this point in the history
…7095)

* feat(router): update router to support query params in intent links

* fix(core): update RouterStateEvent interface

* chore(core): add tests for query param intents
  • Loading branch information
pedrobonamin committed Jul 10, 2024
1 parent 04c374a commit 793b2b0
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 9 deletions.
39 changes: 39 additions & 0 deletions packages/sanity/src/core/studio/router/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {describe, it} from '@jest/globals'
import {expect} from '@playwright/experimental-ct-react'
import {type Tool} from 'sanity'
import {type RouterState} from 'sanity/router'

import {resolveIntentState} from './helpers'

describe('resolveIntentState', () => {
const testTool: Tool = {
name: 'test',
title: 'Test tool',
component: () => null,
canHandleIntent: () => true,
getIntentState: () => ({}),
}

it('should resolve intent state with query params', () => {
const state: RouterState = {
intent: 'edit',
params: {
id: 'p-bay-area-san-francisco-2022-08-17-2022-08-17',
type: 'playlist',
},
_searchParams: [['perspective', 'bundle.pedro-summer']],
}

const resolved = resolveIntentState([testTool], null, state)
expect(resolved).toEqual({
type: 'state',
isNotFound: false,
state: {
// searchParams are persisted in the router state
_searchParams: [['perspective', 'bundle.pedro-summer']],
tool: 'test',
test: {},
},
})
})
})
1 change: 1 addition & 0 deletions packages/sanity/src/core/studio/router/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export function resolveIntentState(

const nextUrlState: Record<string, unknown> = {
...prevState,
_searchParams: nextState._searchParams,
tool: matchingTool.name,
[matchingTool.name]: toolState,
}
Expand Down
3 changes: 2 additions & 1 deletion packages/sanity/src/core/studio/router/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {type BrowserHistory, type HashHistory, type History, type MemoryHistory} from 'history'
import {type RouterState} from 'sanity/router'

export interface RouterStateEvent {
type: 'state'
state: Record<string, unknown> // | null
state: RouterState
isNotFound: boolean
}

Expand Down
33 changes: 33 additions & 0 deletions packages/sanity/src/router/IntentLink.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {describe, expect, it} from '@jest/globals'
import {render} from '@testing-library/react'

import {IntentLink} from './IntentLink'
import {route} from './route'
import {RouterProvider} from './RouterProvider'

describe('IntentLink', () => {
it('should resolve intent link with query params', () => {
const router = route.create('/test', [route.intents('/intent')])
const component = render(
<IntentLink
intent="edit"
params={{
id: 'document-id-123',
type: 'document-type',
}}
searchParams={[['perspective', `bundle.summer-drop`]]}
/>,
{
wrapper: ({children}) => (
<RouterProvider onNavigate={() => null} router={router} state={{}}>
{children}
</RouterProvider>
),
},
)
// Component should render the query param in the href
expect(component.container.querySelector('a')?.href).toContain(
'/test/intent/edit/id=document-id-123;type=document-type/?perspective=bundle.summer-drop',
)
})
})
10 changes: 8 additions & 2 deletions packages/sanity/src/router/IntentLink.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {type ForwardedRef, forwardRef, type HTMLProps} from 'react'

import {type IntentParameters} from './types'
import {type IntentParameters, type SearchParam} from './types'
import {useIntentLink} from './useIntentLink'

/**
Expand All @@ -24,6 +24,11 @@ export interface IntentLinkProps {
* Whether to replace the current URL in the browser history instead of adding a new entry.
*/
replace?: boolean

/**
* search params to include in the intent.
*/
searchParams?: SearchParam[]
}

/**
Expand All @@ -43,12 +48,13 @@ export const IntentLink = forwardRef(function IntentLink(
props: IntentLinkProps & HTMLProps<HTMLAnchorElement>,
ref: ForwardedRef<HTMLAnchorElement>,
) {
const {intent, params, target, ...restProps} = props
const {intent, params, target, searchParams, ...restProps} = props
const {onClick, href} = useIntentLink({
intent,
params,
target,
onClick: props.onClick,
searchParams,
})

return <a {...restProps} href={href} onClick={onClick} ref={ref} target={target} />
Expand Down
10 changes: 8 additions & 2 deletions packages/sanity/src/router/RouterProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
type Router,
type RouterContextValue,
type RouterState,
type SearchParam,
} from './types'

/**
Expand Down Expand Up @@ -80,9 +81,14 @@ export function RouterProvider(props: RouterProviderProps): ReactElement {
const {onNavigate, router: routerProp, state} = props

const resolveIntentLink = useCallback(
(intentName: string, parameters?: IntentParameters): string => {
(intentName: string, parameters?: IntentParameters, _searchParams?: SearchParam[]): string => {
const [params, payload] = Array.isArray(parameters) ? parameters : [parameters]
return routerProp.encode({intent: intentName, params, payload})
return routerProp.encode({
intent: intentName,
params,
payload,
_searchParams,
})
},
[routerProp],
)
Expand Down
6 changes: 5 additions & 1 deletion packages/sanity/src/router/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,11 @@ export interface RouterContextValue {
* Resolves the intent link for the given intent name and parameters.
* See {@link IntentParameters}
*/
resolveIntentLink: (intentName: string, params?: IntentParameters) => string
resolveIntentLink: (
intentName: string,
params?: IntentParameters,
searchParams?: SearchParam[],
) => string

/**
* Navigates to the given URL.
Expand Down
10 changes: 7 additions & 3 deletions packages/sanity/src/router/useIntentLink.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useMemo} from 'react'

import {type IntentParameters} from './types'
import {type IntentParameters, type SearchParam} from './types'
import {useLink} from './useLink'
import {useRouter} from './useRouter'

Expand Down Expand Up @@ -32,6 +32,7 @@ export interface UseIntentLinkOptions {
* The target window or frame to open the link in.
*/
target?: string
searchParams?: SearchParam[]
}

/**
Expand Down Expand Up @@ -59,9 +60,12 @@ export function useIntentLink(options: UseIntentLinkOptions): {
onClick: React.MouseEventHandler<HTMLElement>
href: string
} {
const {intent, onClick: onClickProp, params, replace, target} = options
const {intent, onClick: onClickProp, params, replace, target, searchParams} = options
const {resolveIntentLink} = useRouter()
const href = useMemo(() => resolveIntentLink(intent, params), [intent, params, resolveIntentLink])
const href = useMemo(
() => resolveIntentLink(intent, params, searchParams),
[intent, params, searchParams, resolveIntentLink],
)
const {onClick} = useLink({href, onClick: onClickProp, replace, target})

return {onClick, href}
Expand Down

0 comments on commit 793b2b0

Please sign in to comment.