Skip to content

Commit

Permalink
Update wouter to latest version, ensure all pages have back links
Browse files Browse the repository at this point in the history
  • Loading branch information
davidje13 committed Dec 8, 2024
1 parent ee1d0bb commit fa6e150
Show file tree
Hide file tree
Showing 19 changed files with 110 additions and 76 deletions.
1 change: 1 addition & 0 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
transform: {
'\\.[jt]sx?$': 'babel-jest',
},
transformIgnorePatterns: ['/node_modules/(?!wouter)'], // https://github.com/molefrog/wouter/issues/415
testEnvironment: './src/test-helpers/patched-jsdom.ts',
setupFilesAfterEnv: ['<rootDir>/src/test-helpers/entrypoint.ts'],
};
27 changes: 22 additions & 5 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"react-hook-awaited": "1.x",
"react-hook-final-countdown": "2.x",
"shared-reducer": "5.x",
"wouter": "2.10.1"
"wouter": "3.x"
},
"devDependencies": {
"@babel/core": "7.x",
Expand Down
1 change: 1 addition & 0 deletions frontend/resources/html-template.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="<%= htmlWebpackPlugin.options.title %>">
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Router } from 'wouter';
import staticLocationHook from 'wouter/static-location';
import { memoryLocation } from 'wouter/memory-location';
import { act, render } from 'flexible-testing-library-react';

import { App } from './App';

describe('App', () => {
it('renders without error', async () => {
const location = memoryLocation({ path: '/', record: true });
render(
<Router hook={staticLocationHook('/', { record: true })}>
<Router hook={location.hook}>
<App />
</Router>,
);
Expand Down
12 changes: 5 additions & 7 deletions frontend/src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FC, ReactNode } from 'react';
import type { FC } from 'react';
import { Route, Switch } from 'wouter';
import { RedirectRoute } from './RedirectRoute';
import { Footer } from './Footer';
Expand All @@ -14,9 +14,7 @@ export const App: FC = () => (
<>
<Switch>
<Route path="/sso/:service">
{({ service = '' }): ReactNode => (
<LoginCallback service={decodeURIComponent(service)} />
)}
{({ service }) => <LoginCallback service={service} />}
</Route>
<Route path="/">
<WelcomePage />
Expand All @@ -30,15 +28,15 @@ export const App: FC = () => (
<Route path="/create/import">
<RetroCreatePage showImport />
</Route>
<Route path="/retros/:slug/:rest*">
{({ slug = '' }) => <RetroRouter slug={decodeURIComponent(slug)} />}
<Route path="/retros/:slug/*?">
{({ slug }) => <RetroRouter slug={slug} />}
</Route>

<RedirectRoute path="/retros" to="/" replace />
<RedirectRoute path="/retro/:slug" to="/retros/:slug" replace />
<RedirectRoute path="/:slug" to="/retros/:slug" replace />

<Route path="/:rest*">
<Route>
<NotFoundPage />
</Route>
</Switch>
Expand Down
11 changes: 5 additions & 6 deletions frontend/src/components/RedirectRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { type FC } from 'react';
import { Route, Redirect, HookNavigationOptions, LocationHook } from 'wouter';
import { Route, Redirect, type RedirectProps } from 'wouter';

const groupRx = /:([A-Za-z0-9_]+)([?+*]?)/g;
const makePath = (
pattern: string,
params: Record<string, string | undefined>,
): string => pattern.replace(groupRx, (_, name) => params[name] ?? '');

type RedirectRouteProps = HookNavigationOptions<LocationHook> & {
path: string;
type RedirectRouteProps = RedirectProps & {
to: string;
children?: never;
path: string;
};

export const RedirectRoute: FC<RedirectRouteProps> = ({ to, ...props }) =>
export const RedirectRoute: FC<RedirectRouteProps> = ({ to, path, ...props }) =>
Route({
children: (params) => <Redirect to={makePath(to, params)} {...props} />,
...props,
path,
});
19 changes: 5 additions & 14 deletions frontend/src/components/RetroRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,28 +121,19 @@ export const RetroRouter: FC<PropsT> = ({ slug }) => {
<RetroPage {...retroParams} />
</Route>
<Route path="/retros/:slug/groups/:group">
{({ group = '' }) => (
<RetroPage {...retroParams} group={decodeURIComponent(group)} />
)}
{({ group }) => <RetroPage {...retroParams} group={group} />}
</Route>
<Route path="/retros/:slug/archives">
<ArchiveListPage {...retroParams} />
</Route>
<Route path="/retros/:slug/archives/:archiveId">
{({ archiveId = '' }) => (
<ArchivePage
{...retroParams}
archiveId={decodeURIComponent(archiveId)}
/>
{({ archiveId }) => (
<ArchivePage {...retroParams} archiveId={archiveId} />
)}
</Route>
<Route path="/retros/:slug/archives/:archiveId/groups/:group">
{({ archiveId = '', group = '' }) => (
<ArchivePage
{...retroParams}
archiveId={decodeURIComponent(archiveId)}
group={decodeURIComponent(group)}
/>
{({ archiveId, group }) => (
<ArchivePage {...retroParams} archiveId={archiveId} group={group} />
)}
</Route>
<Route path="/retros/:slug/settings">
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/components/archive-list/ArchiveLink.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router } from 'wouter';
import staticLocationHook from 'wouter/static-location';
import { memoryLocation } from 'wouter/memory-location';
import { render, fireEvent, text } from 'flexible-testing-library-react';
import { formatDateTime } from '../../time/formatters';
import { css } from '../../test-helpers/queries';
Expand All @@ -8,21 +8,22 @@ import { ArchiveLink } from './ArchiveLink';

describe('ArchiveLink', () => {
it('links to the archive', () => {
const locationHook = staticLocationHook('/', { record: true });
const location = memoryLocation({ path: '/', record: true });
const dom = render(
<Router hook={locationHook}>
<Router hook={location.hook}>
<ArchiveLink retroSlug="bar" archiveId="a1" created={0} />
</Router>,
);

fireEvent.click(dom.getBy(css('.archive-link')));

expect(locationHook.history).toEqual(['/', '/retros/bar/archives/a1']);
expect(location.history).toEqual(['/', '/retros/bar/archives/a1']);
});

it('displays the time of the archive', () => {
const location = memoryLocation({ path: '/', record: true });
const dom = render(
<Router hook={staticLocationHook('/', { record: true })}>
<Router hook={location.hook}>
<ArchiveLink retroSlug="bar" archiveId="a1" created={0} />
</Router>,
);
Expand Down
26 changes: 15 additions & 11 deletions frontend/src/components/common/Header.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router } from 'wouter';
import staticLocationHook from 'wouter/static-location';
import { memoryLocation } from 'wouter/memory-location';
import { render, fireEvent } from 'flexible-testing-library-react';
import { staticTitleHook } from '../../test-helpers/staticTitleHook';
import { css } from '../../test-helpers/queries';
Expand All @@ -10,10 +10,11 @@ import { Header } from './Header';
describe('Header', () => {
it('sets the document and page title', () => {
const titleHook = staticTitleHook();
const location = memoryLocation({ path: '/', record: true });

const dom = render(
<TitleContext value={titleHook}>
<Router hook={staticLocationHook('/', { record: true })}>
<Router hook={location.hook}>
<Header documentTitle="doc-title" title="page-title" />
</Router>
</TitleContext>,
Expand All @@ -24,11 +25,11 @@ describe('Header', () => {
});

it('displays a back link if specified', () => {
const locationHook = staticLocationHook('/', { record: true });
const location = memoryLocation({ path: '/', record: true });

const dom = render(
<TitleContext value={staticTitleHook()}>
<Router hook={locationHook}>
<Router hook={location.hook}>
<Header
documentTitle="doc-title"
title="page-title"
Expand All @@ -42,13 +43,14 @@ describe('Header', () => {
expect(backLink).toHaveTextContent('back-label');

fireEvent.click(backLink);
expect(locationHook.history).toEqual(['/', 'back-url']);
expect(location.history).toEqual(['/', 'back-url']);
});

it('displays a menu of links if specified', () => {
const location = memoryLocation({ path: '/', record: true });
const dom = render(
<TitleContext value={staticTitleHook()}>
<Router hook={staticLocationHook('/', { record: true })}>
<Router hook={location.hook}>
<Header
documentTitle="doc-title"
title="page-title"
Expand All @@ -68,9 +70,10 @@ describe('Header', () => {
});

it('skips null menu items', () => {
const location = memoryLocation({ path: '/', record: true });
const dom = render(
<TitleContext value={staticTitleHook()}>
<Router hook={staticLocationHook('/', { record: true })}>
<Router hook={location.hook}>
<Header
documentTitle="doc-title"
title="page-title"
Expand All @@ -90,11 +93,11 @@ describe('Header', () => {
});

it('routes to the given URL when a menu item is clicked', () => {
const locationHook = staticLocationHook('/', { record: true });
const location = memoryLocation({ path: '/', record: true });

const dom = render(
<TitleContext value={staticTitleHook()}>
<Router hook={locationHook}>
<Router hook={location.hook}>
<Header
documentTitle="doc-title"
title="page-title"
Expand All @@ -107,15 +110,16 @@ describe('Header', () => {
const links = dom.getAllBy(css('.menu > *'));

fireEvent.click(links[0]!);
expect(locationHook.history).toEqual(['/', 'url-1']);
expect(location.history).toEqual(['/', 'url-1']);
});

it('invokes the given callback when a menu item is clicked', () => {
const clickCallback = jest.fn().mockName('clickCallback');
const location = memoryLocation({ path: '/', record: true });

const dom = render(
<TitleContext value={staticTitleHook()}>
<Router hook={staticLocationHook('/', { record: true })}>
<Router hook={location.hook}>
<Header
documentTitle="doc-title"
title="page-title"
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/password/PasswordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ export const PasswordPage = ({ slug, retroId }: PropsT): ReactElement => {
if (checkingUser) {
return (
<article className="page-password-user">
<Header documentTitle={`${slug} - Refacto`} title={slug} />
<Header
documentTitle={`${slug} - Refacto`}
title={slug}
backLink={{ label: 'Home', action: '/' }}
/>
<div className="loader">Loading&hellip;</div>
</article>
);
Expand All @@ -67,6 +71,7 @@ export const PasswordPage = ({ slug, retroId }: PropsT): ReactElement => {
<Header
documentTitle={`${slug} - Refacto`}
title={`Password for ${slug}`}
backLink={{ label: 'Home', action: '/' }}
/>
<form onSubmit={handleSubmit}>
{/* 'username' is included for password managers to distinguish between retros */}
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/retro-create/RetroCreatePage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router } from 'wouter';
import staticLocationHook from 'wouter/static-location';
import { memoryLocation } from 'wouter/memory-location';
import { render } from 'flexible-testing-library-react';
import mockElement from 'react-mock-element';

Expand All @@ -9,8 +9,9 @@ jest.mock('../common/Header', () => ({ Header: mockElement('mock-header') }));

describe('RetroCreatePage', () => {
it('renders without error', () => {
const location = memoryLocation({ path: '/', record: true });
render(
<Router hook={staticLocationHook('/', { record: true })}>
<Router hook={location.hook}>
<RetroCreatePage />
</Router>,
);
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/retro-create/RetroCreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ export const RetroCreatePage = memo(({ showImport = false }: PropsT) => {
if (!userToken) {
return (
<article className="page-retro-create">
<Header documentTitle={`${title} - Refacto`} title={title} />
<Header
documentTitle={`${title} - Refacto`}
title={title}
backLink={{ label: 'Home', action: '/' }}
/>
<LoginForm message="Register an account to create a new retro" />
</article>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ export const RetroNotFoundPage = memo(({ slug }: PropsT) => {
if (!userToken) {
return (
<article className="page-retro-not-found">
<Header documentTitle="New Retro - Refacto" title="New Retro" />
<Header
documentTitle="New Retro - Refacto"
title="New Retro"
backLink={{ label: 'Home', action: '/' }}
/>
<LoginForm message="Register an account to create a retro" />
</article>
);
Expand Down
Loading

0 comments on commit fa6e150

Please sign in to comment.