Skip to content

Commit

Permalink
chore(refactor): Router: Splitting things up into smaller files (#9988)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobbe authored Feb 9, 2024
1 parent ed033ee commit 8573771
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 170 deletions.
27 changes: 16 additions & 11 deletions __fixtures__/test-project/web/src/layouts/BlogLayout/BlogLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ type BlogLayoutProps = {
children?: React.ReactNode
}

import { Link, routes } from '@redwoodjs/router'
import { Link, NavLink, routes } from '@redwoodjs/router'

import { useAuth } from 'src/auth'

Expand All @@ -23,48 +23,53 @@ const BlogLayout = ({ children }: BlogLayoutProps) => {
<nav>
<ul className="relative flex items-center font-light">
<li>
<Link
<NavLink
className="rounded px-4 py-2 transition duration-100 hover:bg-blue-600"
activeClassName="py-2 px-4 hover:bg-blue-600 transition duration-100 rounded underline underline-offset-4"
to={routes.about()}
>
About
</Link>
</NavLink>
</li>
<li>
<Link
<NavLink
className="rounded px-4 py-2 transition duration-100 hover:bg-blue-600"
activeClassName="py-2 px-4 hover:bg-blue-600 transition duration-100 rounded underline underline-offset-4"
to={routes.contactUs()}
>
Contact Us
</Link>
</NavLink>
</li>
<li>
<Link
<NavLink
className="rounded px-4 py-2 transition duration-100 hover:bg-blue-600"
activeClassName="py-2 px-4 hover:bg-blue-600 transition duration-100 rounded underline underline-offset-4"
to={routes.posts()}
>
Admin
</Link>
</NavLink>
</li>
{isAuthenticated && (
<li>
<Link
<NavLink
className="rounded px-4 py-2 transition duration-100 hover:bg-blue-600"
activeClassName="py-2 px-4 hover:bg-blue-600 transition duration-100 rounded underline underline-offset-4"
onClick={logOut}
to={''}
>
Log Out
</Link>
</NavLink>
</li>
)}
{!isAuthenticated && (
<li>
<Link
<NavLink
className="rounded px-4 py-2 transition duration-100 hover:bg-blue-600"
activeClassName="py-2 px-4 hover:bg-blue-600 transition duration-100 rounded underline underline-offset-4"
to={routes.login()}
>
Log In
</Link>
</NavLink>
</li>
)}
</ul>
Expand Down
2 changes: 1 addition & 1 deletion packages/router/src/ActivePageContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext } from 'react'

import { createNamedContext } from './createNamedContext'
import type { LocationContextType } from './location'
import { createNamedContext } from './util'

export type LoadingState = 'PRE_SHOW' | 'SHOW_LOADING' | 'DONE'
export type LoadingStateRecord = Record<
Expand Down
2 changes: 1 addition & 1 deletion packages/router/src/AuthenticatedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react'

import { Redirect } from './links'
import { Redirect } from './redirect'
import { routes } from './router'
import { useRouterState } from './router-context'
import type { GeneratedRoutesMap } from './util'
Expand Down
2 changes: 1 addition & 1 deletion packages/router/src/PageLoadingContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useContext, useState } from 'react'

import { createNamedContext } from './util'
import { createNamedContext } from './createNamedContext'

export interface PageLoadingContextInterface {
loading: boolean
Expand Down
2 changes: 1 addition & 1 deletion packages/router/src/__tests__/links.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react'

import { render } from '@testing-library/react'

import { NavLink } from '../links'
import { LocationProvider } from '../location'
import { NavLink } from '../navLink'

function createDummyLocation(pathname: string, search = '') {
return {
Expand Down
2 changes: 1 addition & 1 deletion packages/router/src/__tests__/useMatch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import '@testing-library/jest-dom'

import { render, renderHook as tlrRenderHook } from '@testing-library/react'

import { Link } from '../links'
import { Link } from '../link'
import { LocationProvider } from '../location'
import { useMatch } from '../useMatch'
import { flattenSearchParams } from '../util'
Expand Down
8 changes: 8 additions & 0 deletions packages/router/src/createNamedContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createContext } from 'react'

/** Create a React Context with the given name. */
export function createNamedContext<T>(name: string, defaultValue?: T) {
const Ctx = createContext<T | undefined>(defaultValue)
Ctx.displayName = name
return Ctx
}
4 changes: 3 additions & 1 deletion packages/router/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// latter of which has closely inspired some of this code).

export { navigate, back } from './history'
export { Link, NavLink, Redirect } from './links'
export { NavLink } from './navLink'
export { Link } from './link'
export { useLocation, LocationProvider } from './location'
export { Redirect } from './redirect'
export {
usePageLoadingContext,
PageLoadingContextProvider,
Expand Down
46 changes: 46 additions & 0 deletions packages/router/src/link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client'

// This needs to be a client component because it uses onClick, and the onClick
// event handler can't be serialized when passed as an RSC Flight response

import { forwardRef } from 'react'

import { navigate } from './history'

export interface LinkProps {
to: string
onClick?: React.MouseEventHandler<HTMLAnchorElement>
}

export const Link = forwardRef<
HTMLAnchorElement,
LinkProps & React.AnchorHTMLAttributes<HTMLAnchorElement>
>(({ to, onClick, ...rest }, ref) => (
<a
href={to}
ref={ref}
{...rest}
onClick={(event) => {
if (
event.button !== 0 ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey
) {
return
}

event.preventDefault()

if (onClick) {
const result = onClick(event)
if (typeof result !== 'boolean' || result) {
navigate(to)
}
} else {
navigate(to)
}
}}
/>
))
132 changes: 0 additions & 132 deletions packages/router/src/links.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion packages/router/src/location.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react'

import { createNamedContext } from './createNamedContext'
import { gHistory } from './history'
import type { TrailingSlashesTypes } from './util'
import { createNamedContext } from './util'

export interface LocationContextType {
pathname: string
Expand Down
53 changes: 53 additions & 0 deletions packages/router/src/navLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use client'

import { forwardRef } from 'react'

import { Link, type LinkProps } from './link'
import { useMatch } from './useMatch'
import type { FlattenSearchParams } from './util'
import { flattenSearchParams } from './util'

interface NavLinkProps extends LinkProps {
activeClassName: string
activeMatchParams?: FlattenSearchParams
matchSubPaths?: boolean
}

export const NavLink = forwardRef<
HTMLAnchorElement,
NavLinkProps & React.AnchorHTMLAttributes<HTMLAnchorElement>
>(
(
{
to,
activeClassName,
activeMatchParams,
matchSubPaths,
className,
onClick,
...rest
},
ref
) => {
// Separate pathname and search parameters, USVString expected
const [pathname, queryString] = to.split('?')
const searchParams = activeMatchParams || flattenSearchParams(queryString)
const matchInfo = useMatch(pathname, {
searchParams,
matchSubPaths,
})
const theClassName = [className, matchInfo.match && activeClassName]
.filter(Boolean)
.join(' ')

return (
<Link
ref={ref}
to={to}
onClick={onClick}
className={theClassName}
{...rest}
/>
)
}
)
2 changes: 1 addition & 1 deletion packages/router/src/params.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useContext } from 'react'

import { createNamedContext } from './util'
import { createNamedContext } from './createNamedContext'

export interface ParamsContextProps {
params: Record<string, string>
Expand Down
Loading

0 comments on commit 8573771

Please sign in to comment.