diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index fcb9b4ec0a..0783dce0a4 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -598,6 +598,7 @@ export function useLinkProps< // null for LinkUtils const dest = { + ...(options.to && { from: matchPathname }), ...options, } diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index 551ac382a8..2d1abad62c 100644 --- a/packages/react-router/src/router.ts +++ b/packages/react-router/src/router.ts @@ -1102,27 +1102,14 @@ export class Router< ): ParsedLocation => { let fromPath = this.latestLocation.pathname let fromSearch = dest.fromSearch || this.latestLocation.search - const looseRoutesByPath = this.routesByPath as Record - - const fromRoute = - dest.from !== undefined - ? looseRoutesByPath[trimPathRight(dest.from)] - : undefined const fromMatches = this.matchRoutes( this.latestLocation.pathname, fromSearch, ) - const fromMatch = fromMatches.find((d) => d.routeId === fromRoute?.id) - - fromPath = fromMatch?.pathname || fromPath - - invariant( - dest.from == null || fromMatch != null, - 'Could not find match for from: ' + dest.from, - ) - + fromPath = + fromMatches.find((d) => d.id === dest.from)?.pathname || fromPath fromSearch = last(fromMatches)?.search || this.latestLocation.search const stayingMatches = matches?.filter((d) => @@ -1786,7 +1773,8 @@ export class Router< preload: !!preload, context: parentContext, location, - navigate: (opts: any) => this.navigate({ ...opts }), + navigate: (opts: any) => + this.navigate({ ...opts, from: match.pathname }), buildLocation: this.buildLocation, cause: preload ? 'preload' : match.cause, })) ?? ({} as any) @@ -1839,7 +1827,8 @@ export class Router< abortController: match.abortController, context: match.context, location, - navigate: (opts) => this.navigate({ ...opts } as any), + navigate: (opts) => + this.navigate({ ...opts, from: match.pathname } as any), cause: preload ? 'preload' : match.cause, route, } diff --git a/packages/react-router/src/useNavigate.tsx b/packages/react-router/src/useNavigate.tsx index 93b8bdbd8f..52d048b30e 100644 --- a/packages/react-router/src/useNavigate.tsx +++ b/packages/react-router/src/useNavigate.tsx @@ -29,6 +29,7 @@ export function useNavigate< (options: NavigateOptions) => { return router.navigate({ ...options, + from: options.to ? router.state.resolvedLocation.pathname : undefined, }) }, [router], @@ -62,6 +63,7 @@ export function Navigate< React.useEffect(() => { navigate({ + from: props.to ? match.pathname : undefined, ...props, } as any) // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx index d825131401..9ebcf20454 100644 --- a/packages/react-router/tests/link.test.tsx +++ b/packages/react-router/tests/link.test.tsx @@ -1090,691 +1090,6 @@ describe('Link', () => { expect(await screen.findByText('Params: id1')).toBeInTheDocument() }) - - test('when navigating from /posts/$postId to /posts/$postId/info and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () => { - return ( - -

Index

- Posts - - To first post - -
- ) - }, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - return ( - <> -

Details!

- - To Information - - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsLink = await screen.findByRole('link', { name: 'To first post' }) - - expect(postsLink).toHaveAttribute('href', '/posts/id1/details') - - fireEvent.click(postsLink) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/details') - - const informationLink = await screen.findByRole('link', { - name: 'To Information', - }) - - expect(informationLink).toHaveAttribute('href', '/posts/id1/info') - - fireEvent.click(informationLink) - - expect(await screen.findByText('Information')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/info') - - expect(await screen.findByText('Params: id1')) - }) - - test('when navigating from /posts/$postId to ./info and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () => { - return ( - -

Index

- Posts - - To first post - -
- ) - }, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - return ( - <> -

Details!

- - To Information - - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsLink = await screen.findByRole('link', { name: 'To first post' }) - - expect(postsLink).toHaveAttribute('href', '/posts/id1/details') - - fireEvent.click(postsLink) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/details') - - const informationLink = await screen.findByRole('link', { - name: 'To Information', - }) - - expect(informationLink).toHaveAttribute('href', '/posts/id1/info') - - fireEvent.click(informationLink) - - expect(await screen.findByText('Information')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/info') - - expect(await screen.findByText('Params: id1')) - }) - - test('when navigating from /posts/$postId to ../$postId and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () => { - return ( - -

Index

- Posts - - To first post - -
- ) - }, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - return ( - <> -

Details!

- - To Post - - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsLink = await screen.findByRole('link', { name: 'To first post' }) - - expect(postsLink).toHaveAttribute('href', '/posts/id1/details') - - fireEvent.click(postsLink) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/details') - - const postLink = await screen.findByRole('link', { - name: 'To Post', - }) - - expect(postLink).toHaveAttribute('href', '/posts/id1') - - fireEvent.click(postLink) - - expect(await screen.findByText('Posts')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1') - }) - - test('when navigating from /invoices to ./invoiceId and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () => { - return ( - -

Index

- Posts - - To first post - -
- ) - }, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - return ( - <> -

Details!

- - To Invoices - - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const invoicesRoute = createRoute({ - getParentRoute: () => rootRoute, - path: 'invoices', - component: () => ( - <> -

Invoices!

- - - ), - }) - - const InvoiceComponent = () => { - const params = useParams({ strict: false }) - return ( - <> - invoiceId: {params.invoiceId} - - ) - } - - const invoiceRoute = createRoute({ - getParentRoute: () => invoicesRoute, - path: '$invoiceId', - component: InvoiceComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - invoicesRoute.addChildren([invoiceRoute]), - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsLink = await screen.findByRole('link', { name: 'To first post' }) - - expect(postsLink).toHaveAttribute('href', '/posts/id1/details') - - fireEvent.click(postsLink) - - expect( - await screen.findByText( - 'Invariant failed: Could not find match for from: /invoices', - ), - ).toBeInTheDocument() - }) - - describe('active links', () => { - const makeRouter = ({ - trailingSlash, - basepath, - }: { - trailingSlash: boolean - basepath: string | undefined - }) => { - const rootRoute = createRootRoute({ - component: () => { - return ( - -

Root

- Posts - - To first post - - -
- ) - }, - }) - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: () => { - return ( - -

Index

-
- ) - }, - }) - - const postsRoute = createRoute({ - getParentRoute: () => rootRoute, - path: 'posts', - component: () => { - return ( - -

Posts

- -
- ) - }, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - return createRouter({ - basepath, - trailingSlash: trailingSlash ? 'always' : undefined, - routeTree: rootRoute.addChildren([ - indexRoute, - postsRoute.addChildren([postRoute]), - ]), - }) - } - - describe('no basepath', () => { - test('when on /posts/$postId, Links to /posts should be active', async () => { - const router = makeRouter({ trailingSlash: false, basepath: undefined }) - render() - - fireEvent.click( - await screen.findByRole('link', { name: 'To first post' }), - ) - - const postsLink = await screen.findByRole('link', { name: 'Posts' }) - - expect(router.state.location.pathname).toBe('/posts/id1') - expect(postsLink).toHaveAttribute('data-status', 'active') - expect(postsLink).toHaveAttribute('aria-current', 'page') - }) - test('when on /posts/$postId/, Links to /posts/ should be active (trailingSlash: always)', async () => { - const router = makeRouter({ trailingSlash: true, basepath: undefined }) - - render() - - fireEvent.click( - await screen.findByRole('link', { name: 'To first post' }), - ) - - const postsLink = await screen.findByRole('link', { name: 'Posts' }) - - expect(router.state.location.pathname).toBe('/posts/id1/') - expect(postsLink).toHaveAttribute('data-status', 'active') - expect(postsLink).toHaveAttribute('aria-current', 'page') - }) - }) - describe('with basepath', () => { - test('when on /app/posts/$postId, Links to /posts should be active', async () => { - const router = makeRouter({ trailingSlash: false, basepath: '/app' }) - render() - - fireEvent.click( - await screen.findByRole('link', { name: 'To first post' }), - ) - - const postsLink = await screen.findByRole('link', { name: 'Posts' }) - - expect(router.state.location.pathname).toBe('/app/posts/id1') - expect(postsLink).toHaveAttribute('data-status', 'active') - expect(postsLink).toHaveAttribute('aria-current', 'page') - }) - test('when on /app/posts/$postId/, Links to /posts/ should be active (trailingSlash: always)', async () => { - const router = makeRouter({ trailingSlash: true, basepath: '/app' }) - - render() - - fireEvent.click( - await screen.findByRole('link', { name: 'To first post' }), - ) - - const postsLink = await screen.findByRole('link', { name: 'Posts' }) - - expect(router.state.location.pathname).toBe('/app/posts/id1/') - expect(postsLink).toHaveAttribute('data-status', 'active') - expect(postsLink).toHaveAttribute('aria-current', 'page') - }) - }) - }) }) describe('createLink', () => { diff --git a/packages/react-router/tests/useNavigate.test.tsx b/packages/react-router/tests/useNavigate.test.tsx deleted file mode 100644 index 6c16e9b88b..0000000000 --- a/packages/react-router/tests/useNavigate.test.tsx +++ /dev/null @@ -1,888 +0,0 @@ -import React from 'react' -import '@testing-library/jest-dom/vitest' -import { afterEach, describe, expect, it, test, vi } from 'vitest' -import { cleanup, fireEvent, render, screen } from '@testing-library/react' - -import { - createRootRoute, - createRoute, - createRouter, - useParams, - Outlet, - useNavigate, - RouterProvider, -} from '../src' - -afterEach(() => { - window.history.replaceState(null, 'root', '/') - cleanup() -}) - -test('when navigating to /posts', async () => { - const rootRoute = createRootRoute() - - const IndexComponent = () => { - const navigate = useNavigate() - return ( - -

Index

- - -
- ) - } - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: IndexComponent, - }) - - const postsRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/posts', - component: () => { - return ( - -

Posts

-
- ) - }, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([indexRoute, postsRoute]), - }) - - render() - - const postsButton = await screen.findByRole('button', { name: 'Posts' }) - - fireEvent.click(postsButton) - - expect( - await screen.findByRole('heading', { name: 'Posts' }), - ).toBeInTheDocument() - - expect(window.location.pathname).toBe('/posts') -}) - -test('when navigating from /posts to ./$postId', async () => { - const rootRoute = createRootRoute() - const IndexComponent = () => { - const navigate = useNavigate() - return ( - -

Index

- - -
- ) - } - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: IndexComponent, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => rootRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostsIndexComponent = () => { - const navigate = useNavigate() - return ( - -

Posts Index

- -
- ) - } - - const postsIndexRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '/', - component: PostsIndexComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - const navigate = useNavigate() - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - postsRoute.addChildren([postsIndexRoute, postRoute]), - ]), - }) - - render() - - const postsButton = await screen.findByRole('button', { name: 'Posts' }) - - fireEvent.click(postsButton) - - expect(await screen.findByText('Posts Index')).toBeInTheDocument() - - const postButton = await screen.findByRole('button', { - name: 'To the first post', - }) - - fireEvent.click(postButton) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() - - expect(window.location.pathname).toBe('/posts/id1') -}) - -test('when navigating from /posts to ../posts/$postId', async () => { - const rootRoute = createRootRoute() - - const IndexComponent = () => { - const navigate = useNavigate() - return ( - -

Index

- - -
- ) - } - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: IndexComponent, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => rootRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostsIndexComponent = () => { - const navigate = useNavigate() - return ( - -

Posts Index

- -
- ) - } - - const postsIndexRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '/', - component: PostsIndexComponent, - }) - - const PostComponent = () => { - const navigate = useNavigate() - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - postsRoute.addChildren([postsIndexRoute, postRoute]), - ]), - }) - - render() - - const postsButton = await screen.findByRole('button', { name: 'Posts' }) - - fireEvent.click(postsButton) - - expect(await screen.findByText('Posts Index')).toBeInTheDocument() - - const postButton = await screen.findByRole('button', { - name: 'To the first post', - }) - - fireEvent.click(postButton) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() -}) - -test('when navigating from /posts/$postId to /posts/$postId/info and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const IndexComponent = () => { - const navigate = useNavigate() - return ( - -

Index

- - -
- ) - } - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: IndexComponent, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - const navigate = useNavigate() - return ( - <> -

Details!

- - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsButton = await screen.findByRole('button', { - name: 'To first post', - }) - - fireEvent.click(postsButton) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/details') - - const informationButton = await screen.findByRole('button', { - name: 'To Information', - }) - - fireEvent.click(informationButton) - - expect(await screen.findByText('Information')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/info') - - expect(await screen.findByText('Params: id1')) -}) - -test('when navigating from /posts/$postId to ./info and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const IndexComponent = () => { - const navigate = useNavigate() - return ( - -

Index

- - -
- ) - } - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: IndexComponent, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - const navigate = useNavigate() - return ( - <> -

Details!

- - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsButton = await screen.findByRole('button', { - name: 'To first post', - }) - - fireEvent.click(postsButton) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/details') - - const informationButton = await screen.findByRole('button', { - name: 'To Information', - }) - - fireEvent.click(informationButton) - - expect(await screen.findByText('Information')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/info') - - expect(await screen.findByText('Params: id1')) -}) - -test('when navigating from /posts/$postId to ../$postId and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const IndexComponent = () => { - const navigate = useNavigate() - return ( - -

Index

- - -
- ) - } - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: IndexComponent, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - const navigate = useNavigate() - return ( - <> -

Details!

- - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsButton = await screen.findByRole('button', { - name: 'To first post', - }) - - fireEvent.click(postsButton) - - expect(await screen.findByText('Params: id1')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1/details') - - const postButton = await screen.findByRole('button', { - name: 'To Post', - }) - - fireEvent.click(postButton) - - expect(await screen.findByText('Posts')).toBeInTheDocument() - - expect(window.location.pathname).toEqual('/posts/id1') -}) - -test('when navigating from /invoices to ./invoiceId and the current route is /posts/$postId/details', async () => { - const rootRoute = createRootRoute() - - const IndexComponent = () => { - const navigate = useNavigate() - return ( - -

Index

- - -
- ) - } - - const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: IndexComponent, - }) - - const layoutRoute = createRoute({ - getParentRoute: () => rootRoute, - id: '_layout', - component: () => { - return ( - <> -

Layout

- - - ) - }, - }) - - const PostsComponent = () => { - return ( - -

Posts

- -
- ) - } - - const postsRoute = createRoute({ - getParentRoute: () => layoutRoute, - path: 'posts', - component: PostsComponent, - }) - - const PostComponent = () => { - const params = useParams({ strict: false }) - return ( - - Params: {params.postId} - - - ) - } - - const postRoute = createRoute({ - getParentRoute: () => postsRoute, - path: '$postId', - component: PostComponent, - }) - - const DetailsComponent = () => { - const navigate = useNavigate() - const [error, setError] = React.useState() - return ( - <> -

Details!

- - Something went wrong! - - ) - } - - const detailsRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'details', - component: DetailsComponent, - }) - - const InformationComponent = () => { - return ( - -

Information

-
- ) - } - - const informationRoute = createRoute({ - getParentRoute: () => postRoute, - path: 'info', - component: InformationComponent, - }) - - const invoicesRoute = createRoute({ - getParentRoute: () => rootRoute, - path: 'invoices', - component: () => ( - <> -

Invoices!

- - - ), - }) - - const InvoiceComponent = () => { - const params = useParams({ strict: false }) - return ( - <> - invoiceId: {params.invoiceId} - - ) - } - - const invoiceRoute = createRoute({ - getParentRoute: () => invoicesRoute, - path: '$invoiceId', - component: InvoiceComponent, - }) - - const router = createRouter({ - routeTree: rootRoute.addChildren([ - indexRoute, - layoutRoute.addChildren([ - invoicesRoute.addChildren([invoiceRoute]), - postsRoute.addChildren([ - postRoute.addChildren([detailsRoute, informationRoute]), - ]), - ]), - ]), - }) - - render() - - const postsButton = await screen.findByRole('button', { - name: 'To first post', - }) - - fireEvent.click(postsButton) - - const invoicesButton = await screen.findByRole('button', { - name: 'To Invoices', - }) - - fireEvent.click(invoicesButton) - - expect(await screen.findByText('Something went wrong!')).toBeInTheDocument() -})