From 29e226487de4923d193949db4553b8505dbb418b Mon Sep 17 00:00:00 2001 From: chorobin Date: Mon, 16 Sep 2024 01:23:50 +0200 Subject: [PATCH 1/5] fix: allow activeProps to infer component props --- packages/react-router/src/link.tsx | 67 ++++++++++++++++-------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index 2513d2b6d..5855c8673 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -846,50 +846,67 @@ export function useLinkProps< } } +type UseLinkReactProps = TComp extends keyof JSX.IntrinsicElements + ? JSX.IntrinsicElements[TComp] + : React.PropsWithoutRef< + TComp extends React.ComponentType ? TProps : never + > & + React.RefAttributes< + TComp extends + | React.FC<{ ref: infer TRef }> + | React.Component<{ ref: infer TRef }> + ? TRef + : never + > + export type UseLinkPropsOptions< TRouter extends AnyRouter = RegisteredRouter, TFrom extends RoutePaths | string = string, TTo extends string | undefined = '.', TMaskFrom extends RoutePaths | string = TFrom, TMaskTo extends string = '.', -> = ActiveLinkOptions & - React.AnchorHTMLAttributes +> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & + UseLinkReactProps<'a'> export type ActiveLinkOptions< + TComp = 'a', TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = '.', TMaskFrom extends string = TFrom, TMaskTo extends string = '.', -> = LinkOptions & ActiveLinkOptionProps +> = LinkOptions & + ActiveLinkOptionProps -type ActiveLinkAnchorProps = Omit< - React.AnchorHTMLAttributes & { - [key: `data-${string}`]: unknown - }, - 'children' -> +type ActiveLinkAnchorProps = LinkComponentReactProps & { + [key: `data-${string}`]: unknown +} -export interface ActiveLinkOptionProps { +export interface ActiveLinkOptionProps { /** * A function that returns additional props for the `active` state of this link. * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated) */ - activeProps?: ActiveLinkAnchorProps | (() => ActiveLinkAnchorProps) + activeProps?: + | ActiveLinkAnchorProps + | (() => ActiveLinkAnchorProps) /** * A function that returns additional props for the `inactive` state of this link. * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated) */ - inactiveProps?: ActiveLinkAnchorProps | (() => ActiveLinkAnchorProps) + inactiveProps?: + | ActiveLinkAnchorProps + | (() => ActiveLinkAnchorProps) } export type LinkProps< + TComp = 'a', TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = '.', TMaskFrom extends string = TFrom, TMaskTo extends string = '.', -> = ActiveLinkOptions & +> = ActiveLinkOptions & LinkPropsChildren export interface LinkPropsChildren { @@ -902,32 +919,20 @@ export interface LinkPropsChildren { }) => React.ReactNode) } -type LinkComponentReactProps = React.PropsWithoutRef< - TComp extends React.FC | React.Component - ? TProps - : TComp extends keyof React.JSX.IntrinsicElements - ? Omit, 'children' | 'preload'> - : never -> & - React.RefAttributes< - TComp extends - | React.FC<{ ref: infer TRef }> - | React.Component<{ ref: infer TRef }> - ? TRef - : TComp extends keyof React.JSX.IntrinsicElements - ? React.ComponentRef - : never - > +type LinkComponentReactProps = Omit< + UseLinkReactProps, + 'children' | 'preload' +> export type LinkComponentProps< - TComp, + TComp = 'a', TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = '.', TMaskFrom extends string = TFrom, TMaskTo extends string = '.', > = LinkComponentReactProps & - LinkProps + LinkProps export type LinkComponent = < TRouter extends RegisteredRouter = RegisteredRouter, From f151252ce23c41e84ad08b6df62dc803b2069014 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Thu, 19 Sep 2024 01:05:58 +0200 Subject: [PATCH 2/5] test --- packages/react-router/tests/link.test.tsx | 89 +++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx index 770caac8e..d687975ef 100644 --- a/packages/react-router/tests/link.test.tsx +++ b/packages/react-router/tests/link.test.tsx @@ -3710,4 +3710,93 @@ describe('createLink', () => { expect(customElement.hasAttribute('foo')).toBe(true) expect(customElement.getAttribute('foo')).toBe('bar') }) + + it('should pass activeProps and inactiveProps to the custom link', async () => { + const Button: React.FC< + React.PropsWithChildren<{ + active?: boolean + foo?: boolean + overrideMeIfYouWant: string + }> + > = ({ active, foo, children, ...props }) => ( + + ) + + const ButtonLink = createLink(Button) + + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => ( + <> + + Button1 + + + Button2 + + + Button3 + + + ), + }) + const postsRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/posts', + }) + const router = createRouter({ + routeTree: rootRoute.addChildren([indexRoute, postsRoute]), + }) + + render() + + const button1 = await screen.findByText('active: yes - foo: no - Button1') + expect(button1.getAttribute('data-hello')).toBe('world') + expect(button1.getAttribute('overrideMeIfYouWant')).toBe( + 'overridden-by-activeProps', + ) + + const button2 = await screen.findByText('active: no - foo: yes - Button2') + expect(button2.getAttribute('data-hello')).toBe('void') + expect(button2.getAttribute('overrideMeIfYouWant')).toBe( + 'overridden-by-inactiveProps', + ) + + const button3 = await screen.findByText('active: no - foo: no - Button3') + expect(button3.getAttribute('overrideMeIfYouWant')).toBe('Button3') + }) }) From ba93641ea85d90000232fd79fcb85197066fa55f Mon Sep 17 00:00:00 2001 From: chorobin Date: Thu, 19 Sep 2024 01:12:14 +0200 Subject: [PATCH 3/5] fix: make active props partial --- packages/react-router/src/link.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index 5855c8673..26c1c5a3c 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -878,25 +878,23 @@ export type ActiveLinkOptions< > = LinkOptions & ActiveLinkOptionProps -type ActiveLinkAnchorProps = LinkComponentReactProps & { - [key: `data-${string}`]: unknown -} +type ActiveLinkProps = Partial< + LinkComponentReactProps & { + [key: `data-${string}`]: unknown + } +> export interface ActiveLinkOptionProps { /** * A function that returns additional props for the `active` state of this link. * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated) */ - activeProps?: - | ActiveLinkAnchorProps - | (() => ActiveLinkAnchorProps) + activeProps?: ActiveLinkProps | (() => ActiveLinkProps) /** * A function that returns additional props for the `inactive` state of this link. * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated) */ - inactiveProps?: - | ActiveLinkAnchorProps - | (() => ActiveLinkAnchorProps) + inactiveProps?: ActiveLinkProps | (() => ActiveLinkProps) } export type LinkProps< From 276e40be9f6338a8266307a5846dee315bb3b388 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Thu, 19 Sep 2024 01:27:50 +0200 Subject: [PATCH 4/5] default props shall not override active/inactive props --- packages/react-router/src/link.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index 26c1c5a3c..2d1554fa0 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -819,9 +819,9 @@ export function useLinkProps< } return { + ...rest, ...resolvedActiveProps, ...resolvedInactiveProps, - ...rest, href: disabled ? undefined : next.maskedLocation From c1cbbd1780f2c48bfb300f3e3fe9d321d9fb80bc Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Thu, 19 Sep 2024 01:28:39 +0200 Subject: [PATCH 5/5] fix test --- packages/react-router/tests/link.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx index d687975ef..48ab423a3 100644 --- a/packages/react-router/tests/link.test.tsx +++ b/packages/react-router/tests/link.test.tsx @@ -3748,7 +3748,7 @@ describe('createLink', () => { to="/posts" overrideMeIfYouWant="Button2" activeProps={{ - active: false, + active: true, 'data-hello': 'world', }} inactiveProps={{ @@ -3763,7 +3763,7 @@ describe('createLink', () => { to="/posts" overrideMeIfYouWant="Button3" activeProps={{ - active: false, + active: true, }} inactiveProps={{ active: false,