Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nick/react router v6 #75

Merged
merged 6 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/70)

### Update of React Router from v5 to v6

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/75/files)

## Redux intro

### Pure functions benifits
Expand Down
4,541 changes: 2,154 additions & 2,387 deletions package-lock.json

Large diffs are not rendered by default.

25 changes: 12 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@
},
"homepage": "https://github.com/nickovchinnikov/minesweeper#readme",
"devDependencies": {
"@babel/core": "^7.17.0",
"@babel/core": "^7.17.2",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@emotion/jest": "^11.7.1",
"@storybook/addon-actions": "^6.4.18",
"@storybook/addon-essentials": "^6.4.18",
"@storybook/addon-links": "^6.4.18",
"@storybook/builder-webpack5": "^6.4.18",
"@storybook/manager-webpack5": "^6.4.18",
"@storybook/react": "^6.4.18",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-links": "^6.4.19",
"@storybook/builder-webpack5": "^6.4.19",
"@storybook/manager-webpack5": "^6.4.19",
"@storybook/react": "^6.4.19",
"@stryker-mutator/core": "^5.6.1",
"@stryker-mutator/jest-runner": "^5.6.1",
"@testing-library/jest-dom": "^5.16.2",
Expand All @@ -57,21 +57,20 @@
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/react-redux": "^7.1.22",
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"@typescript-eslint/eslint-plugin": "^5.12.0",
"@typescript-eslint/parser": "^5.12.0",
"babel-loader": "^8.2.3",
"chromatic": "^6.4.3",
"css-loader": "^6.6.0",
"eslint": "^8.8.0",
"eslint": "^8.9.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-storybook": "^0.5.6",
"html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4",
"jest": "^27.5.0",
"jest": "^27.5.1",
"jest-canvas-mock": "^2.3.1",
"prettier": "^2.5.1",
"reveal-md": "^5.3.2",
Expand All @@ -90,6 +89,6 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.6",
"react-router-dom": "^5.3.0"
"react-router-dom": "^6.2.1"
}
}
12 changes: 9 additions & 3 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { render } from '@testing-library/react';

import { App, Home } from './App';

jest.mock('@/hooks/useQuery', () => ({
__esModule: true,
useQuery: () => ({ get: () => null }),
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: jest.fn(),
useSearchParams: jest.fn().mockReturnValue([
{
get: () => null,
},
jest.fn(),
]),
}));

describe('App test cases', () => {
Expand Down
78 changes: 49 additions & 29 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { FC, Suspense } from 'react';
import { Provider } from 'react-redux';
import { Location } from 'history';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Switch,
BrowserRouter,
Routes,
Route,
Link,
Redirect,
Navigate,
useSearchParams,
} from 'react-router-dom';

import { useQuery } from '@/hooks/useQuery';
import { store } from '@/store';

const MinesweeperWithHooks = React.lazy(() =>
Expand All @@ -35,8 +35,8 @@ const MinesweeperWithReactRedux = React.lazy(() =>
);

export const Navigation: FC = () => {
const query = useQuery();
const level = query.get('level') || '';
const [searchParams] = useSearchParams();
const level = searchParams.get('level') || '';

const getLocationObjWithSearchParams = (
pathname: string
Expand All @@ -57,17 +57,17 @@ export const Navigation: FC = () => {
<Link to={getLocationObjWithSearchParams('/')}>Home</Link>
</li>
<li>
<Link to={getLocationObjWithSearchParams('/game-with-hooks')}>
<Link to={getLocationObjWithSearchParams('/minesweeper/hooks')}>
Game With Hooks
</Link>
</li>
<li>
<Link to={getLocationObjWithSearchParams('/game-with-usereducer')}>
<Link to={getLocationObjWithSearchParams('/minesweeper/usereducer')}>
Game With useReducer
</Link>
</li>
<li>
<Link to={getLocationObjWithSearchParams('/game-with-reactredux')}>
<Link to={getLocationObjWithSearchParams('/minesweeper/reactredux')}>
Game With ReactRedux
</Link>
</li>
Expand All @@ -78,37 +78,57 @@ export const Navigation: FC = () => {

export const Home: FC = () => <h2>Minesweeper game Forever!</h2>;

export const App: FC = () => (
<Provider store={store}>
<Router>
<Navigation />
<Switch>
<Route path="/game-with-hooks/:username?">
export const Routing: FC = () => (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/minesweeper">
<Route
path="hooks"
element={
<Suspense fallback={<div>Loading minesweeper with hooks...</div>}>
<MinesweeperWithHooks />
</Suspense>
</Route>
<Route path="/game-with-usereducer">
}
>
<Route
path=":username"
element={
<Suspense fallback={<div>Loading minesweeper with hooks...</div>}>
<MinesweeperWithHooks />
</Suspense>
}
/>
</Route>
<Route
path="usereducer"
element={
<Suspense
fallback={<div>Loading minesweeper with useReducer...</div>}
>
<MinesweeperWithUseReducer />
</Suspense>
</Route>
<Route path="/game-with-reactredux">
}
/>
<Route
path="reactredux"
element={
<Suspense
fallback={<div>Loading minesweeper with ReactRedux...</div>}
>
<MinesweeperWithReactRedux />
</Suspense>
</Route>
<Route path="/">
<Home />
</Route>
<Route path="*">
<Redirect to="/" />
</Route>
</Switch>
</Router>
}
/>
</Route>
<Route path="*" element={<Navigate to="/" />} />
</Routes>
);

export const App: FC = () => (
<Provider store={store}>
<BrowserRouter>
<Navigation />
<Routing />
</BrowserRouter>
</Provider>
);
6 changes: 3 additions & 3 deletions src/__snapshots__/App.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ exports[`App test cases App renders check 1`] = `
</li>
<li>
<a
href="/game-with-hooks"
href="/minesweeper/hooks"
>
Game With Hooks
</a>
</li>
<li>
<a
href="/game-with-usereducer"
href="/minesweeper/usereducer"
>
Game With useReducer
</a>
</li>
<li>
<a
href="/game-with-reactredux"
href="/minesweeper/reactredux"
>
Game With ReactRedux
</a>
Expand Down
18 changes: 0 additions & 18 deletions src/hooks/useQuery.test.ts

This file was deleted.

4 changes: 0 additions & 4 deletions src/hooks/useQuery.ts

This file was deleted.

6 changes: 4 additions & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { render } from 'react-dom';

import { App } from './App';

ReactDOM.render(<App />, document.getElementById('root'));
const rootElement = document.getElementById('root');

render(<App />, rootElement);
13 changes: 10 additions & 3 deletions src/modules/GameWithHooks/GameWithHooks.snap.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import React from 'react';
import { useSearchParams } from 'react-router-dom';
import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';

import { GameWithHooks } from './GameWithHooks';

jest.mock('@/hooks/useQuery', () => ({
__esModule: true,
useQuery: () => ({ get: () => null }),
const mockSetSearchParams = jest.fn();

jest.mock('react-router-dom', () => ({
useSearchParams: jest.fn(),
}));

(useSearchParams as jest.Mock).mockReturnValue([
{ get: () => null },
mockSetSearchParams,
]);

it('Render game field by default', () => {
const { asFragment } = render(<GameWithHooks />);
userEvent.click(screen.getByTestId('0,0'), { button: 2 });
Expand Down
27 changes: 11 additions & 16 deletions src/modules/GameWithHooks/GameWithHooks.test.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
import React from 'react';
import { useSearchParams } from 'react-router-dom';
import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';

import { useQuery } from '@/hooks/useQuery';

import { GameWithHooks } from './GameWithHooks';

const mockOnClick = jest.fn();
const mockOnChangeLevel = jest.fn();
const mockOnReset = jest.fn();
const mockOnContextMenu = jest.fn();
const mockHistoryPush = jest.fn();
const mockSetSearchParams = jest.fn();

jest.mock('react-router-dom', () => ({
useHistory: () => ({
push: mockHistoryPush,
}),
}));

jest.mock('@/hooks/useQuery', () => ({
__esModule: true,
useQuery: jest.fn(),
useSearchParams: jest.fn(),
}));

(useQuery as jest.Mock).mockReturnValue({ get: () => null });
(useSearchParams as jest.Mock).mockReturnValue([
{ get: () => null },
mockSetSearchParams,
]);

jest.mock('./useGame', () => ({
__esModule: true,
Expand Down Expand Up @@ -73,12 +68,12 @@ describe('GameWithHooks test cases', () => {
render(<GameWithHooks />);
userEvent.selectOptions(screen.getByRole('combobox'), 'intermediate');
expect(mockOnChangeLevel).toHaveBeenCalled();
expect(mockHistoryPush).toHaveBeenCalledWith({
search: `?${new URLSearchParams({ level: 'intermediate' }).toString()}`,
});
expect(mockSetSearchParams).toHaveBeenCalledWith({ level: 'intermediate' });
});
it('Level in search params works fine', () => {
(useQuery as jest.Mock).mockReturnValue({ get: () => 'intermediate' });
(useSearchParams as jest.Mock).mockReturnValue([
{ get: () => 'intermediate' },
]);
render(<GameWithHooks />);
const intermediateOption = screen.queryByText('intermediate');
expect(intermediateOption).toBeInTheDocument();
Expand Down
13 changes: 5 additions & 8 deletions src/modules/GameWithHooks/GameWithHooks.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import React, { FC, useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { useSearchParams } from 'react-router-dom';

import { GameLevels, LevelNames } from '@/modules/GameSettings';

import { Scoreboard } from '@/components/Scoreboard';
import { Grid } from '@/components/Grid';
import { GameOver } from '@/components/Game';
import { useQuery } from '@/hooks/useQuery';

import { useGame } from './useGame';

export const GameWithHooks: FC = () => {
const history = useHistory();
const query = useQuery();
const urlLevelParam = (query.get('level') || undefined) as LevelNames;
const [searchParams, setSearchParams] = useSearchParams();

const urlLevelParam = (searchParams.get('level') || undefined) as LevelNames;

const {
level,
Expand All @@ -33,9 +32,7 @@ export const GameWithHooks: FC = () => {

const onChangeLevelHandler = useCallback(
({ target: { value: level } }: React.ChangeEvent<HTMLSelectElement>) => {
history.push({
search: `?${new URLSearchParams({ level }).toString()}`,
});
setSearchParams({ level });
onChangeLevel(level as LevelNames);
},
// Stryker disable next-line ArrayDeclaration
Expand Down
Loading