Skip to content

Commit e52673b

Browse files
authored
fix(router): hash history links with target="_blank" generate incorrect URLs (#5242)
1 parent 31e5b9c commit e52673b

File tree

4 files changed

+109
-3
lines changed

4 files changed

+109
-3
lines changed

packages/react-router/src/link.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,13 @@ export function useLinkProps<
120120
let external = false
121121
if (router.origin) {
122122
if (href.startsWith(router.origin)) {
123-
href = href.replace(router.origin, '') || '/'
123+
href = router.history.createHref(href.replace(router.origin, '')) || '/'
124124
} else {
125125
external = true
126126
}
127127
}
128128
return { href, external }
129-
}, [disabled, next.maskedLocation, next.url, router.origin])
129+
}, [disabled, next.maskedLocation, next.url, router.origin, router.history])
130130

131131
const externalLink = React.useMemo(() => {
132132
if (hrefOption?.external) {

packages/react-router/tests/link.test.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
Outlet,
1818
RouterProvider,
1919
createBrowserHistory,
20+
createHashHistory,
2021
createLink,
2122
createMemoryHistory,
2223
createRootRoute,
@@ -6335,3 +6336,55 @@ describe('rewrite', () => {
63356336
expect(appLink).toHaveAttribute('href', 'http://app.example.com/')
63366337
})
63376338
})
6339+
6340+
describe('hash history with target="_blank" links', () => {
6341+
test('should generate correct href for target="_blank" links in hash history mode', async () => {
6342+
const hashHistory = createHashHistory()
6343+
6344+
const rootRoute = createRootRoute()
6345+
const indexRoute = createRoute({
6346+
getParentRoute: () => rootRoute,
6347+
path: '/',
6348+
component: () => {
6349+
return (
6350+
<>
6351+
<h1>Index</h1>
6352+
<Link data-testid="posts-link" to="/posts">
6353+
Posts (same tab)
6354+
</Link>
6355+
<Link data-testid="about-blank-link" to="/about" target="_blank">
6356+
About (new tab)
6357+
</Link>
6358+
</>
6359+
)
6360+
},
6361+
})
6362+
6363+
const postsRoute = createRoute({
6364+
getParentRoute: () => rootRoute,
6365+
path: '/posts',
6366+
component: () => <h1>Posts</h1>,
6367+
})
6368+
6369+
const aboutRoute = createRoute({
6370+
getParentRoute: () => rootRoute,
6371+
path: '/about',
6372+
component: () => <h1>About</h1>,
6373+
})
6374+
6375+
const router = createRouter({
6376+
routeTree: rootRoute.addChildren([indexRoute, postsRoute, aboutRoute]),
6377+
history: hashHistory,
6378+
})
6379+
6380+
render(<RouterProvider router={router} />)
6381+
6382+
const postsLink = await screen.findByTestId('posts-link')
6383+
expect(postsLink).toHaveAttribute('href', '/#/posts')
6384+
expect(postsLink).not.toHaveAttribute('target', '_blank')
6385+
6386+
const postsBlankLink = await screen.findByTestId('about-blank-link')
6387+
expect(postsBlankLink).toHaveAttribute('href', '/#/about')
6388+
expect(postsBlankLink).toHaveAttribute('target', '_blank')
6389+
})
6390+
})

packages/solid-router/src/link.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export function useLinkProps<
146146
let external = false
147147
if (router.origin) {
148148
if (href.startsWith(router.origin)) {
149-
href = href.replace(router.origin, '')
149+
href = router.history.createHref(href.replace(router.origin, ''))
150150
} else {
151151
external = true
152152
}

packages/solid-router/tests/link.test.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
Outlet,
1616
RouterProvider,
1717
createBrowserHistory,
18+
createHashHistory,
1819
createLink,
1920
createMemoryHistory,
2021
createRootRoute,
@@ -5674,3 +5675,55 @@ describe('relative links to from route', () => {
56745675
},
56755676
)
56765677
})
5678+
5679+
describe('hash history with target="_blank" links', () => {
5680+
test('should generate correct href for target="_blank" links in hash history mode', async () => {
5681+
const hashHistory = createHashHistory()
5682+
5683+
const rootRoute = createRootRoute()
5684+
const indexRoute = createRoute({
5685+
getParentRoute: () => rootRoute,
5686+
path: '/',
5687+
component: () => {
5688+
return (
5689+
<>
5690+
<h1>Index</h1>
5691+
<Link data-testid="posts-link" to="/posts">
5692+
Posts (same tab)
5693+
</Link>
5694+
<Link data-testid="about-blank-link" to="/about" target="_blank">
5695+
About (new tab)
5696+
</Link>
5697+
</>
5698+
)
5699+
},
5700+
})
5701+
5702+
const postsRoute = createRoute({
5703+
getParentRoute: () => rootRoute,
5704+
path: '/posts',
5705+
component: () => <h1>Posts</h1>,
5706+
})
5707+
5708+
const aboutRoute = createRoute({
5709+
getParentRoute: () => rootRoute,
5710+
path: '/about',
5711+
component: () => <h1>About</h1>,
5712+
})
5713+
5714+
const router = createRouter({
5715+
routeTree: rootRoute.addChildren([indexRoute, postsRoute, aboutRoute]),
5716+
history: hashHistory,
5717+
})
5718+
5719+
render(() => <RouterProvider router={router} />)
5720+
5721+
const postsLink = await screen.findByTestId('posts-link')
5722+
expect(postsLink).toHaveAttribute('href', '/#/posts')
5723+
expect(postsLink).not.toHaveAttribute('target', '_blank')
5724+
5725+
const postsBlankLink = await screen.findByTestId('about-blank-link')
5726+
expect(postsBlankLink).toHaveAttribute('href', '/#/about')
5727+
expect(postsBlankLink).toHaveAttribute('target', '_blank')
5728+
})
5729+
})

0 commit comments

Comments
 (0)