-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a0be7f3
commit b42b6eb
Showing
24 changed files
with
653 additions
and
355 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,3 +25,7 @@ dist-ssr | |
|
||
# Generated code | ||
generated | ||
|
||
# Copied readmes | ||
packages/cli/README.md | ||
packages/runtime/README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
"typescript.format.semicolons": "remove", | ||
"cSpell.words": [ | ||
"subroute", | ||
"Subroute" | ||
"Subroute", | ||
"testid" | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { useEffect, useRef } from 'react' | ||
import { CitronNavigator } from './CitronNavigator' | ||
import { AnyRoute, Route } from './Route' | ||
import { RequiredKeysOf } from './types' | ||
|
||
type RouteProps<T extends AnyRoute | undefined> = { to?: T } & (T extends Route<any, infer Params, any> | ||
? Params extends void | ||
? unknown | ||
: RequiredKeysOf<Params> extends never ? { params?: Params } : { params: Params } | ||
: unknown) | ||
|
||
type Props<T extends AnyRoute | undefined> = React.AnchorHTMLAttributes<HTMLAnchorElement> & RouteProps<T> | ||
|
||
interface LinkFn { | ||
<T extends AnyRoute | undefined>(props: Props<T>): React.ReactNode, | ||
} | ||
|
||
export const Link: LinkFn = (props) => { | ||
const { to, params, href, children, target, ...anchorProps } = props as Props<Route<any, object, any>> | ||
const actualHref = to ? to.$link(params) : href | ||
const ref = useRef<HTMLAnchorElement>(null) | ||
|
||
useEffect(() => { | ||
const isHashUrl = actualHref && /^\/?#/.test(actualHref) | ||
if (!ref.current || isHashUrl || (target && target != '_self')) return | ||
|
||
function navigate(event: Event) { | ||
event.preventDefault() | ||
history.pushState(null, '', actualHref) | ||
// since we called event.preventDefault(), we now must manually trigger a navigation update | ||
CitronNavigator.instance?.updateRoute?.() | ||
} | ||
|
||
function onKeyPress(event: KeyboardEvent) { | ||
if (event.key != 'Enter') return | ||
navigate(event) | ||
} | ||
|
||
ref.current.addEventListener('click', navigate) | ||
ref.current.addEventListener('keydown', onKeyPress) | ||
|
||
return () => { | ||
ref.current?.removeEventListener('click', navigate) | ||
ref.current?.removeEventListener('keydown', onKeyPress) | ||
} | ||
}, [actualHref, ref.current]) | ||
|
||
return <a ref={ref} href={actualHref} target={target} {...anchorProps}>{children}</a> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
export { CitronNavigator } from './CitronNavigator' | ||
export { Link } from './Link' | ||
export { AnyRoute, Route } from './Route' | ||
export * from './errors' | ||
export { AnyRouteWithParams } from './types' | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { fireEvent, render } from '@testing-library/react' | ||
import { Link } from '../src/Link' | ||
import { NavigatorMock } from './NavigatorMock' | ||
import { RootRoute } from './routes' | ||
import { delay } from './utils' | ||
|
||
describe('Link', () => { | ||
const root = new RootRoute() | ||
const navigator = new NavigatorMock(root) | ||
|
||
beforeEach(() => { | ||
navigator.reset() | ||
history.replaceState(null, '', 'http://localhost/') | ||
}) | ||
|
||
describe('URLs without hash', () => { | ||
beforeAll(() => { | ||
navigator.useHash = false | ||
}) | ||
|
||
test('should render an anchor tag with the correct attributes', async () => { | ||
const rendered = render(<Link data-testid="link" href="/test" className="class">Test</Link>) | ||
const anchor = await rendered.findByTestId('link') | ||
expect(anchor.tagName).toBe('A') | ||
expect(anchor.getAttribute('href')).toBe('/test') | ||
expect(anchor.getAttribute('class')).toBe('class') | ||
}) | ||
|
||
async function shouldNavigateAndNotRefresh(event: Event) { | ||
const rendered = render(<Link data-testid="link" href="/test">Test</Link>) | ||
const anchor = await rendered.findByTestId('link') | ||
event.preventDefault = jest.fn(event.preventDefault) | ||
fireEvent(anchor, event) | ||
await delay() | ||
expect(navigator.updateRoute).toHaveBeenCalled() | ||
expect(location.href).toBe('http://localhost/test') | ||
expect(event.preventDefault).toHaveBeenCalled() | ||
} | ||
|
||
test('should navigate and not refresh when clicked', () => shouldNavigateAndNotRefresh(new MouseEvent('click'))) | ||
|
||
test( | ||
'should navigate and not refresh when enter is pressed', | ||
() => shouldNavigateAndNotRefresh(new KeyboardEvent('keydown', { key: 'Enter' })), | ||
) | ||
|
||
test('should create link from route and params', async () => { | ||
const rendered = render(<Link data-testid="link" to={root.studios.studio} params={{ studioId: '1' }}>Studio 1</Link>) | ||
const anchor = await rendered.findByTestId('link') | ||
expect(anchor.getAttribute('href')).toBe('/studios/1') | ||
}) | ||
|
||
test('should open in another context (target != _self)', async () => { | ||
const rendered = render(<Link data-testid="link" href="/test" target="_blank">Test</Link>) | ||
const anchor = await rendered.findByTestId('link') | ||
const event = new MouseEvent('click') | ||
event.preventDefault = jest.fn(event.preventDefault) | ||
fireEvent(anchor, event) | ||
await delay() | ||
expect(navigator.updateRoute).not.toHaveBeenCalled() | ||
expect(location.href).toBe('http://localhost/') | ||
expect(event.preventDefault).not.toHaveBeenCalled() | ||
}) | ||
}) | ||
|
||
describe('URLs with hash', () => { | ||
beforeAll(() => { | ||
navigator.useHash = true | ||
}) | ||
|
||
test('should render an anchor tag with the correct attributes', async () => { | ||
const rendered = render(<Link data-testid="link" href="/#/test" className="class">Test</Link>) | ||
const anchor = await rendered.findByTestId('link') | ||
expect(anchor.tagName).toBe('A') | ||
expect(anchor.getAttribute('href')).toBe('/#/test') | ||
expect(anchor.getAttribute('class')).toBe('class') | ||
}) | ||
|
||
test('should act like simple anchor when clicked', async () => { | ||
const rendered = render(<Link data-testid="link" href="/#/test">Test</Link>) | ||
const anchor = await rendered.findByTestId('link') | ||
const event = new MouseEvent('click') | ||
event.preventDefault = jest.fn(event.preventDefault) | ||
fireEvent(anchor, event) | ||
await delay() | ||
expect(navigator.updateRoute).not.toHaveBeenCalled() | ||
expect(location.href).toBe('http://localhost/#/test') | ||
expect(event.preventDefault).not.toHaveBeenCalled() | ||
}) | ||
|
||
test('should create link from route and params', async () => { | ||
const rendered = render(<Link data-testid="link" to={root.studios.studio} params={{ studioId: '1' }}>Studio 1</Link>) | ||
const anchor = await rendered.findByTestId('link') | ||
expect(anchor.getAttribute('href')).toBe('/#/studios/1') | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.