Skip to content

Commit f5def45

Browse files
[core] Use router specific Link components (#4661)
1 parent b6bb68e commit f5def45

File tree

8 files changed

+50
-3
lines changed

8 files changed

+50
-3
lines changed

docs/data/toolpad/core/components/page-container/CustomPageContainer.js

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ function Content({ router }) {
4242

4343
Content.propTypes = {
4444
router: PropTypes.shape({
45+
Link: PropTypes.func,
4546
navigate: PropTypes.func.isRequired,
4647
pathname: PropTypes.string.isRequired,
4748
searchParams: PropTypes.instanceOf(URLSearchParams).isRequired,

docs/pages/toolpad/core/api/app-provider.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"router": {
2424
"type": {
2525
"name": "shape",
26-
"description": "{ navigate: func, pathname: string, searchParams: URLSearchParams }"
26+
"description": "{ Link?: func, navigate: func, pathname: string, searchParams: URLSearchParams }"
2727
},
2828
"default": "null"
2929
},

packages/toolpad-core/src/AppProvider/AppProvider.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
RouterContext,
1111
WindowContext,
1212
} from '../shared/context';
13+
import type { LinkProps } from '../shared/Link';
1314
import { AppThemeProvider } from './AppThemeProvider';
1415
import { LocalizationProvider, type LocaleText } from './LocalizationProvider';
1516

@@ -28,6 +29,7 @@ export interface Router {
2829
pathname: string;
2930
searchParams: URLSearchParams;
3031
navigate: Navigate;
32+
Link?: React.ComponentType<LinkProps>;
3133
}
3234

3335
export interface Branding {
@@ -254,6 +256,7 @@ AppProvider.propTypes /* remove-proptypes */ = {
254256
* @default null
255257
*/
256258
router: PropTypes.shape({
259+
Link: PropTypes.func,
257260
navigate: PropTypes.func.isRequired,
258261
pathname: PropTypes.string.isRequired,
259262
searchParams: PropTypes.instanceOf(URLSearchParams).isRequired,

packages/toolpad-core/src/nextjs/NextAppProvider.test.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ vi.mock('next/router', () => ({ useRouter: () => null }));
2424

2525
vi.mock('next/compat/router', () => ({ useRouter: () => null }));
2626

27+
vi.mock('next/link', () => ({ default: () => null }));
28+
2729
interface RouterTestProps {
2830
children: React.ReactNode;
2931
}

packages/toolpad-core/src/nextjs/NextAppProviderApp.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import * as React from 'react';
2+
import NextLink from 'next/link';
23
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
4+
import { LinkProps } from '../shared/Link';
35
import { AppProvider } from '../AppProvider';
46
import type { AppProviderProps, Navigate, Router } from '../AppProvider';
57

8+
const Link = React.forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
9+
const { href, history, ...rest } = props;
10+
return <NextLink ref={ref} href={href} replace={history === 'replace'} {...rest} />;
11+
});
612
/**
713
* @ignore - internal component.
814
*/
@@ -29,6 +35,7 @@ export function NextAppProviderApp(props: AppProviderProps) {
2935
pathname,
3036
searchParams,
3137
navigate,
38+
Link,
3239
}),
3340
[pathname, navigate, searchParams],
3441
);

packages/toolpad-core/src/nextjs/NextAppProviderPages.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import * as React from 'react';
2+
import NextLink from 'next/link';
23
import { asArray } from '@toolpad/utils/collections';
34
import { useRouter } from 'next/router';
5+
import { LinkProps } from '../shared/Link';
46
import { AppProvider } from '../AppProvider';
57
import type { AppProviderProps, Navigate, Router } from '../AppProvider';
68

9+
const Link = React.forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
10+
const { href, history, ...rest } = props;
11+
return <NextLink ref={ref} href={href} replace={history === 'replace'} {...rest} />;
12+
});
13+
714
/**
815
* @ignore - internal component.
916
*/
@@ -41,6 +48,7 @@ export function NextAppProviderPages(props: AppProviderProps) {
4148
pathname,
4249
searchParams,
4350
navigate,
51+
Link,
4452
}),
4553
[navigate, pathname, searchParams],
4654
);

packages/toolpad-core/src/react-router/ReactRouterAppProvider.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
'use client';
22
import * as React from 'react';
3-
import { useSearchParams, useLocation, useNavigate } from 'react-router';
3+
import { useSearchParams, useLocation, useNavigate, Link as ReactRouterLink } from 'react-router';
44
import { AppProvider, type AppProviderProps, Navigate, Router } from '../AppProvider/AppProvider';
5+
import { LinkProps } from '../shared/Link';
6+
7+
const Link = React.forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
8+
const { href, history, ...rest } = props;
9+
return <ReactRouterLink ref={ref} to={href} replace={history === 'replace'} {...rest} />;
10+
});
511

612
function ReactRouterAppProvider(props: AppProviderProps) {
713
const { pathname } = useLocation();
@@ -26,6 +32,7 @@ function ReactRouterAppProvider(props: AppProviderProps) {
2632
pathname,
2733
searchParams,
2834
navigate: navigateImpl,
35+
Link,
2936
}),
3037
[pathname, searchParams, navigateImpl],
3138
);

packages/toolpad-core/src/shared/Link.tsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ import { RouterContext } from './context';
66
*/
77

88
export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
9+
/*
10+
* "replace" will replace the history stack with the URL being navigated to
11+
* "push" will push the URL being navigated to as a new entry onto the history stack
12+
* "auto" is the default and the mimics the "push" behaviour
13+
*/
914
history?: 'auto' | 'push' | 'replace';
15+
href: string;
1016
}
1117

12-
export const Link = React.forwardRef(function Link(
18+
export const DefaultLink = React.forwardRef(function Link(
1319
props: LinkProps,
1420
ref: React.ForwardedRef<HTMLAnchorElement>,
1521
) {
@@ -34,3 +40,16 @@ export const Link = React.forwardRef(function Link(
3440
</a>
3541
);
3642
});
43+
44+
export const Link = React.forwardRef(function Link(
45+
props: LinkProps,
46+
ref: React.ForwardedRef<HTMLAnchorElement>,
47+
) {
48+
const routerContext = React.useContext(RouterContext);
49+
const LinkComponent = routerContext?.Link ?? DefaultLink;
50+
return (
51+
<LinkComponent ref={ref} {...props}>
52+
{props.children}
53+
</LinkComponent>
54+
);
55+
});

0 commit comments

Comments
 (0)