Skip to content

feat(router-core,history): Global blocking status & proceed/reset actions #4398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions e2e/react-router/basic-file-based/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Route as StructuralSharingEnabledRouteImport } from './routes/structura
import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default'
import { Route as RedirectTargetRouteImport } from './routes/redirect/$target'
import { Route as PostsPostIdRouteImport } from './routes/posts.$postId'
import { Route as GlobalBlockerLayoutRouteImport } from './routes/global-blocker/_layout'
import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2'
import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside'
import { Route as groupInsideRouteImport } from './routes/(group)/inside'
Expand All @@ -48,13 +49,20 @@ import { Route as ParamsPsWildcardSplatRouteImport } from './routes/params-ps/wi
import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './routes/params-ps/named/{$foo}suffix'
import { Route as ParamsPsNamedPrefixChar123fooChar125RouteImport } from './routes/params-ps/named/prefix{$foo}'
import { Route as ParamsPsNamedFooRouteImport } from './routes/params-ps/named/$foo'
import { Route as GlobalBlockerLayoutMultiBlockersRouteImport } from './routes/global-blocker/_layout.multi-blockers'
import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b'
import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a'
import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside'
import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout'

const GlobalBlockerRouteImport = createFileRoute('/global-blocker')()
const groupRouteImport = createFileRoute('/(group)')()

const GlobalBlockerRoute = GlobalBlockerRouteImport.update({
id: '/global-blocker',
path: '/global-blocker',
getParentRoute: () => rootRouteImport,
} as any)
const groupRoute = groupRouteImport.update({
id: '/(group)',
getParentRoute: () => rootRouteImport,
Expand Down Expand Up @@ -140,6 +148,10 @@ const PostsPostIdRoute = PostsPostIdRouteImport.update({
path: '/$postId',
getParentRoute: () => PostsRoute,
} as any)
const GlobalBlockerLayoutRoute = GlobalBlockerLayoutRouteImport.update({
id: '/_layout',
getParentRoute: () => GlobalBlockerRoute,
} as any)
const LayoutLayout2Route = LayoutLayout2RouteImport.update({
id: '/_layout-2',
getParentRoute: () => LayoutRoute,
Expand Down Expand Up @@ -251,6 +263,12 @@ const ParamsPsNamedFooRoute = ParamsPsNamedFooRouteImport.update({
path: '/params-ps/named/$foo',
getParentRoute: () => rootRouteImport,
} as any)
const GlobalBlockerLayoutMultiBlockersRoute =
GlobalBlockerLayoutMultiBlockersRouteImport.update({
id: '/multi-blockers',
path: '/multi-blockers',
getParentRoute: () => GlobalBlockerLayoutRoute,
} as any)
const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({
id: '/layout-b',
path: '/layout-b',
Expand Down Expand Up @@ -283,6 +301,7 @@ export interface FileRoutesByFullPath {
'/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute
'/inside': typeof groupInsideRoute
'/lazyinside': typeof groupLazyinsideRoute
'/global-blocker': typeof GlobalBlockerLayoutRouteWithChildren
'/posts/$postId': typeof PostsPostIdRoute
'/redirect/$target': typeof RedirectTargetRouteWithChildren
'/search-params/default': typeof SearchParamsDefaultRoute
Expand All @@ -295,6 +314,7 @@ export interface FileRoutesByFullPath {
'/subfolder/inside': typeof groupSubfolderInsideRoute
'/layout-a': typeof LayoutLayout2LayoutARoute
'/layout-b': typeof LayoutLayout2LayoutBRoute
'/global-blocker/multi-blockers': typeof GlobalBlockerLayoutMultiBlockersRoute
'/params-ps/named/$foo': typeof ParamsPsNamedFooRoute
'/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route
'/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute
Expand All @@ -321,6 +341,7 @@ export interface FileRoutesByTo {
'/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute
'/inside': typeof groupInsideRoute
'/lazyinside': typeof groupLazyinsideRoute
'/global-blocker': typeof GlobalBlockerLayoutRouteWithChildren
'/posts/$postId': typeof PostsPostIdRoute
'/search-params/default': typeof SearchParamsDefaultRoute
'/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute
Expand All @@ -332,6 +353,7 @@ export interface FileRoutesByTo {
'/subfolder/inside': typeof groupSubfolderInsideRoute
'/layout-a': typeof LayoutLayout2LayoutARoute
'/layout-b': typeof LayoutLayout2LayoutBRoute
'/global-blocker/multi-blockers': typeof GlobalBlockerLayoutMultiBlockersRoute
'/params-ps/named/$foo': typeof ParamsPsNamedFooRoute
'/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route
'/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute
Expand Down Expand Up @@ -365,6 +387,8 @@ export interface FileRoutesById {
'/(group)/inside': typeof groupInsideRoute
'/(group)/lazyinside': typeof groupLazyinsideRoute
'/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren
'/global-blocker': typeof GlobalBlockerRouteWithChildren
'/global-blocker/_layout': typeof GlobalBlockerLayoutRouteWithChildren
'/posts/$postId': typeof PostsPostIdRoute
'/redirect/$target': typeof RedirectTargetRouteWithChildren
'/search-params/default': typeof SearchParamsDefaultRoute
Expand All @@ -377,6 +401,7 @@ export interface FileRoutesById {
'/(group)/subfolder/inside': typeof groupSubfolderInsideRoute
'/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute
'/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute
'/global-blocker/_layout/multi-blockers': typeof GlobalBlockerLayoutMultiBlockersRoute
'/params-ps/named/$foo': typeof ParamsPsNamedFooRoute
'/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route
'/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute
Expand Down Expand Up @@ -407,6 +432,7 @@ export interface FileRouteTypes {
| '/onlyrouteinside'
| '/inside'
| '/lazyinside'
| '/global-blocker'
| '/posts/$postId'
| '/redirect/$target'
| '/search-params/default'
Expand All @@ -419,6 +445,7 @@ export interface FileRouteTypes {
| '/subfolder/inside'
| '/layout-a'
| '/layout-b'
| '/global-blocker/multi-blockers'
| '/params-ps/named/$foo'
| '/params-ps/named/prefix{$foo}'
| '/params-ps/named/{$foo}suffix'
Expand All @@ -445,6 +472,7 @@ export interface FileRouteTypes {
| '/onlyrouteinside'
| '/inside'
| '/lazyinside'
| '/global-blocker'
| '/posts/$postId'
| '/search-params/default'
| '/structural-sharing/$enabled'
Expand All @@ -456,6 +484,7 @@ export interface FileRouteTypes {
| '/subfolder/inside'
| '/layout-a'
| '/layout-b'
| '/global-blocker/multi-blockers'
| '/params-ps/named/$foo'
| '/params-ps/named/prefix{$foo}'
| '/params-ps/named/{$foo}suffix'
Expand Down Expand Up @@ -488,6 +517,8 @@ export interface FileRouteTypes {
| '/(group)/inside'
| '/(group)/lazyinside'
| '/_layout/_layout-2'
| '/global-blocker'
| '/global-blocker/_layout'
| '/posts/$postId'
| '/redirect/$target'
| '/search-params/default'
Expand All @@ -500,6 +531,7 @@ export interface FileRouteTypes {
| '/(group)/subfolder/inside'
| '/_layout/_layout-2/layout-a'
| '/_layout/_layout-2/layout-b'
| '/global-blocker/_layout/multi-blockers'
| '/params-ps/named/$foo'
| '/params-ps/named/prefix{$foo}'
| '/params-ps/named/{$foo}suffix'
Expand Down Expand Up @@ -529,6 +561,7 @@ export interface RootRouteChildren {
Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route
anotherGroupOnlyrouteinsideRoute: typeof anotherGroupOnlyrouteinsideRoute
groupRoute: typeof groupRouteWithChildren
GlobalBlockerRoute: typeof GlobalBlockerRouteWithChildren
RedirectTargetRoute: typeof RedirectTargetRouteWithChildren
StructuralSharingEnabledRoute: typeof StructuralSharingEnabledRoute
ParamsPsIndexRoute: typeof ParamsPsIndexRoute
Expand All @@ -550,6 +583,13 @@ export interface RootRouteChildren {

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/global-blocker': {
id: '/global-blocker'
path: '/global-blocker'
fullPath: '/global-blocker'
preLoaderRoute: typeof GlobalBlockerRouteImport
parentRoute: typeof rootRouteImport
}
'/(group)': {
id: '/(group)'
path: '/'
Expand Down Expand Up @@ -669,6 +709,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof PostsPostIdRouteImport
parentRoute: typeof PostsRoute
}
'/global-blocker/_layout': {
id: '/global-blocker/_layout'
path: '/global-blocker'
fullPath: '/global-blocker'
preLoaderRoute: typeof GlobalBlockerLayoutRouteImport
parentRoute: typeof GlobalBlockerRoute
}
'/_layout/_layout-2': {
id: '/_layout/_layout-2'
path: ''
Expand Down Expand Up @@ -816,6 +863,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ParamsPsNamedFooRouteImport
parentRoute: typeof rootRouteImport
}
'/global-blocker/_layout/multi-blockers': {
id: '/global-blocker/_layout/multi-blockers'
path: '/multi-blockers'
fullPath: '/global-blocker/multi-blockers'
preLoaderRoute: typeof GlobalBlockerLayoutMultiBlockersRouteImport
parentRoute: typeof GlobalBlockerLayoutRoute
}
'/_layout/_layout-2/layout-b': {
id: '/_layout/_layout-2/layout-b'
path: '/layout-b'
Expand Down Expand Up @@ -925,6 +979,29 @@ const groupRouteChildren: groupRouteChildren = {

const groupRouteWithChildren = groupRoute._addFileChildren(groupRouteChildren)

interface GlobalBlockerLayoutRouteChildren {
GlobalBlockerLayoutMultiBlockersRoute: typeof GlobalBlockerLayoutMultiBlockersRoute
}

const GlobalBlockerLayoutRouteChildren: GlobalBlockerLayoutRouteChildren = {
GlobalBlockerLayoutMultiBlockersRoute: GlobalBlockerLayoutMultiBlockersRoute,
}

const GlobalBlockerLayoutRouteWithChildren =
GlobalBlockerLayoutRoute._addFileChildren(GlobalBlockerLayoutRouteChildren)

interface GlobalBlockerRouteChildren {
GlobalBlockerLayoutRoute: typeof GlobalBlockerLayoutRouteWithChildren
}

const GlobalBlockerRouteChildren: GlobalBlockerRouteChildren = {
GlobalBlockerLayoutRoute: GlobalBlockerLayoutRouteWithChildren,
}

const GlobalBlockerRouteWithChildren = GlobalBlockerRoute._addFileChildren(
GlobalBlockerRouteChildren,
)

interface RedirectTargetRouteChildren {
RedirectTargetViaBeforeLoadRoute: typeof RedirectTargetViaBeforeLoadRoute
RedirectTargetViaLoaderRoute: typeof RedirectTargetViaLoaderRoute
Expand Down Expand Up @@ -953,6 +1030,7 @@ const rootRouteChildren: RootRouteChildren = {
Char45824Char54620Char48124Char44397Route,
anotherGroupOnlyrouteinsideRoute: anotherGroupOnlyrouteinsideRoute,
groupRoute: groupRouteWithChildren,
GlobalBlockerRoute: GlobalBlockerRouteWithChildren,
RedirectTargetRoute: RedirectTargetRouteWithChildren,
StructuralSharingEnabledRoute: StructuralSharingEnabledRoute,
ParamsPsIndexRoute: ParamsPsIndexRoute,
Expand Down
8 changes: 8 additions & 0 deletions e2e/react-router/basic-file-based/src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ function RootComponent() {
>
This Route Does Not Exist
</Link>
<Link
to="/global-blocker/multi-blockers"
activeProps={{
className: 'font-bold',
}}
>
Multi Blockers
</Link>
</div>
<hr />
<Outlet />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react'
import { createFileRoute, useBlocker } from '@tanstack/react-router'

export const Route = createFileRoute('/global-blocker/_layout/multi-blockers')({
component: MultiBlockersPage,
})

function MultiBlockersPage() {
const blocker1 = useBlocker({
shouldBlockFn: async () => Promise.resolve(true),
enableBeforeUnload: true,
disabled: false,
withResolver: true,
})

const blocker2 = useBlocker({
shouldBlockFn: async () => Promise.resolve(true),
enableBeforeUnload: true,
disabled: false,
withResolver: true,
})

return (
<div>
<h1>This page always blocks navigation</h1>
<div data-testid="blocker-1-status">blocker1 is {blocker1.status}</div>
<div data-testid="blocker-2-status">blocker2 is {blocker2.status}</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
Outlet,
createFileRoute,
useNavigationBlockingState,
} from '@tanstack/react-router'

export const Route = createFileRoute('/global-blocker/_layout')({
component: RouteComponent,
})

function RouteComponent() {
const { proceed, reset, status, proceedAll } = useNavigationBlockingState()

return (
<div>
{status === 'blocked' ? (
<div
style={{
marginLeft: 'auto',
paddingRight: '240px',
position: 'fixed',
border: '1px solid black',
width: '600px',
height: '600px',
left: '50px',
bottom: '50px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
gap: '10px',
fontSize: '20px',
color: 'white',
borderRadius: '10px',
boxShadow: '0 0 10px rgba(0,0,0,0.5)',
zIndex: 1000,
backgroundColor: 'saddlebrown',
}}
>
<h3>Global Blocking Modal</h3>
<div>Navigation is blocked</div>
<div className="flex gap-2 flex-col">
<button onClick={proceed}>Proceed</button>
<button onClick={proceedAll}>Proceed All</button>
<button onClick={reset}>Reset</button>
</div>
</div>
) : null}
<Outlet />
</div>
)
}
Loading