mirror of
https://github.com/tuono-labs/tuono
synced 2026-07-25 12:52:47 -07:00
feat(packages/tuono-router): add replace method to router and replace prop to Link (#596)
This commit is contained in:
@@ -4,10 +4,14 @@ import { render, fireEvent, screen } from '@testing-library/react'
|
||||
import Link from './Link'
|
||||
|
||||
const pushMock = vi.fn()
|
||||
const replaceMock = vi.fn()
|
||||
const preloadMock = vi.fn()
|
||||
|
||||
vi.mock('../hooks/useRouter', () => ({
|
||||
useRouter: (): { push: typeof pushMock } => ({ push: pushMock }),
|
||||
useRouter: (): { push: typeof pushMock; replace: typeof replaceMock } => ({
|
||||
push: pushMock,
|
||||
replace: replaceMock,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('../hooks/useRoute', () => ({
|
||||
@@ -51,6 +55,19 @@ describe('Link Component', () => {
|
||||
expect(pushMock).toHaveBeenCalledWith('/test', { scroll: true })
|
||||
})
|
||||
|
||||
it('calls router.replace on click when the replace prop is true', () => {
|
||||
render(
|
||||
<Link href="/test" replace>
|
||||
Test Link
|
||||
</Link>,
|
||||
)
|
||||
const link = screen.getByRole('link')
|
||||
|
||||
fireEvent.click(link)
|
||||
expect(replaceMock).toHaveBeenCalledWith('/test', { scroll: true })
|
||||
expect(pushMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not navigate if href starts with "#"', () => {
|
||||
render(<Link href="#section">Anchor Link</Link>)
|
||||
const link = screen.getByRole('link')
|
||||
|
||||
@@ -16,6 +16,12 @@ interface TuonoLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
* @default true
|
||||
*/
|
||||
scroll?: boolean
|
||||
|
||||
/**
|
||||
* If "true" the history entry will be replaced instead of pushed.
|
||||
* @default false
|
||||
*/
|
||||
replace?: boolean
|
||||
}
|
||||
|
||||
function isEventModifierKeyActiveAndTargetDifferentFromSelf(
|
||||
@@ -39,6 +45,7 @@ export default function Link(
|
||||
scroll = true,
|
||||
children,
|
||||
href,
|
||||
replace,
|
||||
onClick,
|
||||
...rest
|
||||
} = componentProps
|
||||
@@ -68,7 +75,9 @@ export default function Link(
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
router.push(href || '', { scroll })
|
||||
const method = replace ? 'replace' : 'push'
|
||||
|
||||
router[method](href || '', { scroll })
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,10 @@ import React from 'react'
|
||||
|
||||
import { useRouterContext } from '../components/RouterContext'
|
||||
|
||||
interface PushOptions {
|
||||
type NavigationType = 'pushState' | 'replaceState'
|
||||
type NavigationFn = (path: string, opts?: NavigationOptions) => void
|
||||
|
||||
interface NavigationOptions {
|
||||
/**
|
||||
* If "false" the scroll offset will be kept across page navigation. Default "true"
|
||||
*/
|
||||
@@ -13,7 +16,13 @@ interface UseRouterResult {
|
||||
/**
|
||||
* Redirects to the path passed as argument updating the browser history.
|
||||
*/
|
||||
push: (path: string, opt?: PushOptions) => void
|
||||
push: NavigationFn
|
||||
|
||||
/**
|
||||
* Redirects to the path passed as argument replacing the current history
|
||||
* entry.
|
||||
*/
|
||||
replace: NavigationFn
|
||||
|
||||
/**
|
||||
* This object contains all the query params of the current route
|
||||
@@ -29,9 +38,9 @@ interface UseRouterResult {
|
||||
export const useRouter = (): UseRouterResult => {
|
||||
const { location, updateLocation } = useRouterContext()
|
||||
|
||||
const push = React.useCallback(
|
||||
(path: string, opt?: PushOptions): void => {
|
||||
const { scroll = true } = opt || {}
|
||||
const navigate = React.useCallback(
|
||||
(type: NavigationType, path: string, opts?: NavigationOptions): void => {
|
||||
const { scroll = true } = opts || {}
|
||||
const url = new URL(path, window.location.origin)
|
||||
|
||||
updateLocation({
|
||||
@@ -41,7 +50,8 @@ export const useRouter = (): UseRouterResult => {
|
||||
searchStr: url.search,
|
||||
hash: url.hash,
|
||||
})
|
||||
history.pushState(path, '', path)
|
||||
|
||||
history[type](path, '', path)
|
||||
|
||||
if (scroll) {
|
||||
window.scroll(0, 0)
|
||||
@@ -50,8 +60,23 @@ export const useRouter = (): UseRouterResult => {
|
||||
[updateLocation],
|
||||
)
|
||||
|
||||
const push = React.useCallback(
|
||||
(path: string, opts?: NavigationOptions): void => {
|
||||
navigate('pushState', path, opts)
|
||||
},
|
||||
[navigate],
|
||||
)
|
||||
|
||||
const replace = React.useCallback(
|
||||
(path: string, opts?: NavigationOptions): void => {
|
||||
navigate('replaceState', path, opts)
|
||||
},
|
||||
[navigate],
|
||||
)
|
||||
|
||||
return {
|
||||
push,
|
||||
replace,
|
||||
query: location.search,
|
||||
pathname: location.pathname,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user