Skip to content

Commit

Permalink
Merge pull request #8 from maxholman/interpolate
Browse files Browse the repository at this point in the history
Named route path interpolation and hook navigation fixes
  • Loading branch information
maxholman authored Dec 26, 2022
2 parents 0b230b7 + 3e60fe0 commit 2a26b63
Show file tree
Hide file tree
Showing 22 changed files with 942 additions and 906 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module.exports = {
root: true,
extends: '@block65/eslint-config/react',
extends: [
'@block65/eslint-config/typescript',
'@block65/eslint-config/react',
],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json',
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ distclean: clean
.PHONY: test
test: node_modules
NODE_OPTIONS=--experimental-vm-modules yarn jest
$(MAKE) build/index.js
$(MAKE) build/main.js

.PRECIOUS: yarn.lock
node_modules: yarn.lock package.json
yarn install

.PHONY: dev
dev: node_modules webpack.config.js
yarn webpack -o build --mode=development -w
dev: node_modules
yarn vite dev

.PHONY: types
types: node_modules
yarn tsc --emitDeclarationOnly --removeComments false

build/main.js: node_modules $(SRCS)
yarn vite build
NODE_ENV=production yarn vite build
npx bundlesize
52 changes: 8 additions & 44 deletions __tests__/__snapshots__/link.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,66 +7,30 @@ exports[`basic 1`] = `
>
test as string
</a>
<a
href="https://invalid.example.com"
rel="no-opener noreferrer"
>
cross origin link
</a>
<a
href="/login"
>
test as fragment
</a>
<a
data-linkylink="true"
href="/login"
>
<span
data-linkylink="true"
>
test nested in an FC
</span>
</a>
<a
href="/login"
>
this will not work properly
</a>
<a
href="/login"
>
plnk plonk
this text should be in a component that has a href
</a>
this text will not be in a component that has a href :sadface:
<a
href="/login"
id="123"
>
Hello!
This anchor should have a href!
</a>
this text will not be in a component that has a href :sadface:
<a
href="/login"
>
<div
data-compycomp="true"
>
Once
<div
data-compycomp="true"
>
Twice
<div
data-compycomp="true"
>
Three times a lady
</div>
</div>
</div>
</a>
<a
href="/login"
href="https://invalid.example.com"
rel="no-opener noreferrer"
>
this will also not work properly
cross origin link
</a>
,
</DocumentFragment>
`;
52 changes: 52 additions & 0 deletions __tests__/interpolate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import '@testing-library/jest-dom';
import { interpolate } from '../lib/named-route.js';

// type PathPart = `/${string}`;
// type Path = `${PathPart}${PathPart}?` | `${PathPart}${PathPart}*`;

test('custom route', async () => {
expect(interpolate('/test', {})).toBe('/test');

expect(
interpolate('/foo/:foo', {
foo: 'oof',
}),
).toBe('/foo/oof');

expect(
interpolate('/foo/:foo?', {
foo: 'oof',
}),
).toBe('/foo/oof');

expect(interpolate('/foo/:foo?', {})).toBe('/foo');
expect(
interpolate('/foo/:foo?/bar/:bar', {
bar: 'rab',
}),
).toBe('/foo/bar/rab');

expect(
interpolate('/foo/:foo*', {
foo: 'oof',
}),
).toBe('/foo/oof');

expect(
interpolate('/foo/:foo+', {
foo: 'oof',
}),
).toBe('/foo/oof');

expect(
interpolate('/foo/:foo*', {
foo: 'oof/rab/zab',
}),
).toBe('/foo/oof/rab/zab');
expect(
interpolate('/set/:set/q/:id?', {
set: 'BTT',
id: 'YES!',
}),
).toBe('/set/BTT/q/YES!');
});
47 changes: 19 additions & 28 deletions __tests__/link.test.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,55 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import type { AnchorHTMLAttributes, FC, PropsWithChildren } from 'react';
import type { FC, PropsWithChildren } from 'react';
import { FormattedMessage, IntlProvider } from 'react-intl';
import type { LinkChildProps } from '../lib/components/Link.js';
import { namedRoute } from '../lib/named-route.js';
import { Link, Router } from '../src/index.js';

const LinkyLink: FC<
PropsWithChildren<AnchorHTMLAttributes<HTMLAnchorElement>>
> = (props) => <span data-linkylink {...props} />;
const ComponentThatTakesProps: FC<PropsWithChildren<LinkChildProps>> = (
props,
) => <a data-linkylink {...props} />;

const CompyComp: FC<PropsWithChildren> = ({ children, ...props }) => (
<div data-compycomp {...props}>
{children}
</div>
const ComponentThatIgnoresProps: FC<PropsWithChildren> = ({ children }) => (
<>{children}</>
);

const BrokeyBroke: FC<PropsWithChildren> = ({ children }) => <>{children}</>;

test('basic', async () => {
const login = namedRoute('/login');

const { asFragment } = render(
<IntlProvider locale="en" onError={() => {}}>
<Router>
<Link dest={login.build()}>test as string</Link>
<Link dest="https://invalid.example.com">cross origin link</Link>
<Link dest={login.build()}>
<>test as fragment</>
</Link>
<Link dest={login.build()}>
<LinkyLink>test nested in an FC</LinkyLink>
<ComponentThatTakesProps>
this text should be in a component that has a href
</ComponentThatTakesProps>
</Link>

<Link dest={login.build()}>
<FormattedMessage
id="broken"
description="this will not work properly"
defaultMessage="this will not work properly"
description="broken"
defaultMessage="this text will not be in a component that has a href :sadface:"
/>
</Link>

<Link dest={login.build()}>plnk plonk</Link>

<Link dest={login.build()}>
<a id="123">Hello!</a>
<a id="123">This anchor should have a href!</a>
</Link>

<Link dest={login.build()}>
<CompyComp>
Once
<CompyComp>
Twice
<CompyComp>Three times a lady</CompyComp>
</CompyComp>
</CompyComp>
</Link>
<Link dest={login.build()}>
<BrokeyBroke>this will also not work properly</BrokeyBroke>
<ComponentThatIgnoresProps>
this text will not be in a component that has a href :sadface:
</ComponentThatIgnoresProps>
</Link>

<Link dest="https://invalid.example.com">cross origin link</Link>
</Router>
,
</IntlProvider>,
);

Expand Down
20 changes: 8 additions & 12 deletions __tests__/navigate.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { FC, useEffect } from 'react';
import { Route } from '../lib/components.js';
import { namedRoute } from '../lib/named-route.js';
import { Router, useNavigate } from '../lib/router.js';
import { Routes } from '../lib/routes.js';
import { Route, Routes } from '../lib/routes.js';
import { LocationDisplay } from './index.test.js';

const usersView = namedRoute('/users/:userId');
Expand All @@ -18,6 +17,7 @@ const Buttons: FC = () => {
{users.map((userId) => (
<li key={userId}>
<button
type="button"
data-testid={`button-${userId}`}
onClick={() => {
const dest = usersView.build({ params: { userId } });
Expand Down Expand Up @@ -46,18 +46,18 @@ const NavigationInsideEffect: FC = () => {
};

test('nav with clicks', async () => {
// const { debug } =
render(
<Router pathname="/users/test1">
<LocationDisplay />
<Routes>
<Route path={usersView.path}>
{(params) => (
<Route
path={usersView.path}
component={(params) => (
<h1 data-testid={`heading-${params.userId}`}>
You are user {params.userId}
</h1>
)}
</Route>
/>

<Route>
<h1>fail</h1>
Expand All @@ -68,8 +68,6 @@ test('nav with clicks', async () => {
</Router>,
);

// debug();

await waitFor(() => screen.getByTestId('heading-test1'));

fireEvent.click(screen.getByTestId('button-alice'));
Expand All @@ -92,11 +90,9 @@ test.only('programmatic navigation with hooks', async () => {
await waitFor(() => {
// global location
expect(location.href).toBe('http://localhost/pickles?foo=bar');
});

await waitFor(() =>
expect(screen.getByTestId('location-display')).toHaveTextContent(
'http://localhost/pickles?foo=bar',
),
);
);
});
});
29 changes: 17 additions & 12 deletions __tests__/nested.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import '@testing-library/jest-dom';
import { render, screen, waitFor } from '@testing-library/react';
import type { FC } from 'react';
import { namedRoute } from '../lib/named-route.js';
import { Route, RouteComponentProps, Router, Routes } from '../src/index.js';
import { Route, Router, Routes } from '../src/index.js';
import { LocationDisplay } from './index.test.js';

test('wildcard routes + nested', async () => {
const userRoot = namedRoute('/users');
const userView = namedRoute('/users/blah/:userId');

const ComponentWithUserId: FC<RouteComponentProps<{ userId: string }>> = ({
params: { userId },
}) => <>userId = {userId}</>;
const ComponentWithUserId: FC<{ userId: string }> = ({ userId }) => (
<>userId = {userId}</>
);

const ParamlessComponent: FC = () => <>I am a Paramless Component</>;

Expand All @@ -23,14 +23,17 @@ test('wildcard routes + nested', async () => {
<h1>inside userRoot</h1>
<Routes>
<Route path={userRoot.path}>this should not display</Route>
<Route wildcard path={userView.path}>
{(params) => (
<Route
wildcard
path={userView.path}
component={(params) => (
<h1 data-testid="users">You are user {params.userId}</h1>
)}
</Route>
/>
<Route path={userView.path} component={ComponentWithUserId} />
<Route component={ParamlessComponent} />
<Route>{() => <ParamlessComponent />}</Route>
<Route>
<ParamlessComponent />
</Route>
<Route>
<h1>fail</h1>
</Route>
Expand Down Expand Up @@ -60,11 +63,13 @@ test('default route + wildcard routes + nested', async () => {
<Routes>
<Route>
<Routes>
<Route wildcard path={userView.path}>
{(params) => (
<Route
wildcard
path={userView.path}
component={(params) => (
<h1 data-testid="users">You are user {params.userId}</h1>
)}
</Route>
/>
<Route>
<h1>fail</h1>
</Route>
Expand Down
Loading

0 comments on commit 2a26b63

Please sign in to comment.