Skip to content

Commit

Permalink
Merge pull request #47 from nickovchinnikov/nick/refactoring2
Browse files Browse the repository at this point in the history
Nick/refactoring2
  • Loading branch information
nickovchinnikov committed Oct 4, 2021
2 parents 27431e6 + dc11701 commit 9b6889c
Show file tree
Hide file tree
Showing 10 changed files with 31,215 additions and 157 deletions.
31,175 changes: 31,092 additions & 83 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"husky": "^7.0.2",
"jest": "^27.2.3",
"prettier": "^2.4.1",
"stryker-cli": "^1.0.2",
"style-loader": "^3.3.0",
"typescript": "^4.4.3",
"url-loader": "^4.1.1",
Expand Down
52 changes: 32 additions & 20 deletions src/components/Grid/Cell.test.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import React from 'react';
import { render, screen, fireEvent, createEvent } from '@testing-library/react';

import { CellState, Coords } from '@/core/Field';
import { CellState, Cell as CellType, Coords } from '@/core/Field';

import { Cell, ClosedFrame, isActiveCell } from './Cell';
import { Cell, ClosedFrame, isActiveCell, areEqual } from './Cell';

describe('Cell component check', () => {
const coords: Coords = [1, 1];
const props = {
coords,
flagCounter: 0,
bombs: 10,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

for (let cell = CellState.empty; cell <= CellState.weakFlag; cell++) {
it('Cell renders correct', () => {
const props = {
coords,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

const { asFragment } = render(<Cell {...props}>{cell}</Cell>);

expect(asFragment()).toMatchSnapshot();
Expand All @@ -25,12 +26,6 @@ describe('Cell component check', () => {
expect(asFragment()).toMatchSnapshot();
});
it('Check prevent default contextMenu for every type of cell', () => {
const props = {
coords,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

render(<Cell {...props}>{cell}</Cell>);

const cellComp = screen.getByTestId(`${coords}`);
Expand All @@ -42,12 +37,6 @@ describe('Cell component check', () => {
});

it('onClick and onContextMenu handler should be called for active cells', () => {
const props = {
coords,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

render(<Cell {...props}>{cell}</Cell>);

const cellComp = screen.getByTestId(`${coords}`);
Expand All @@ -64,4 +53,27 @@ describe('Cell component check', () => {
}
});
}
it('Check areEqual', () => {
const prevProps = {
...props,
children: 0 as CellType,
};

expect(areEqual(prevProps, { ...prevProps })).toBe(true);

expect(areEqual(prevProps, { ...prevProps, coords: [2, 1] })).toBe(false);
expect(areEqual(prevProps, { ...prevProps, coords: [1, 2] })).toBe(false);
expect(areEqual(prevProps, { ...prevProps, coords: [2, 2] })).toBe(false);
expect(areEqual(prevProps, { ...prevProps, coords: [1, 0] })).toBe(false);

expect(areEqual(prevProps, { ...prevProps, children: 1 as CellType })).toBe(
false
);
expect(areEqual(prevProps, { ...prevProps, onClick: jest.fn() })).toBe(
false
);
expect(
areEqual(prevProps, { ...prevProps, onContextMenu: jest.fn() })
).toBe(false);
});
});
24 changes: 21 additions & 3 deletions src/components/Grid/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, memo } from 'react';
import styled from '@emotion/styled';

import { Cell as CellType, Coords, CellState } from '@/core/Field';
Expand Down Expand Up @@ -27,7 +27,23 @@ export interface CellProps {
export const isActiveCell = (cell: CellType): boolean =>
[CellState.hidden, CellState.flag, CellState.weakFlag].includes(cell);

export const Cell: FC<CellProps> = ({ children, coords, ...rest }) => {
export const areEqual = (
prevProps: CellProps,
nextProps: CellProps
): boolean => {
const areEqualCoords =
prevProps.coords.filter((coord, idx) => nextProps.coords[idx] !== coord)
.length === 0;

return (
prevProps.children === nextProps.children &&
areEqualCoords &&
prevProps.onClick === nextProps.onClick &&
prevProps.onContextMenu === nextProps.onContextMenu
);
};

export const Cell: FC<CellProps> = memo(({ children, coords, ...rest }) => {
const [mouseDown, onMouseDown, onMouseUp] = useMouseDown();

const onClick = () => rest.onClick(coords);
Expand Down Expand Up @@ -55,7 +71,9 @@ export const Cell: FC<CellProps> = ({ children, coords, ...rest }) => {
};

return <ComponentsMap {...props}>{children}</ComponentsMap>;
};
}, areEqual);

Cell.displayName = 'Cell';

interface ComponentsMapProps {
children: CellType;
Expand Down
8 changes: 5 additions & 3 deletions src/components/Scoreboard/Counter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, memo } from 'react';
import styled from '@emotion/styled';

export interface CounterProps {
Expand All @@ -8,9 +8,11 @@ export interface CounterProps {
children: string;
}

export const Counter: FC<CounterProps> = ({ children }) => (
export const Counter: FC<CounterProps> = memo(({ children }) => (
<Frame>{children}</Frame>
);
));

Counter.displayName = 'Counter';

const Frame = styled.div`
display: inline-block;
Expand Down
8 changes: 5 additions & 3 deletions src/components/Scoreboard/Level.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, ChangeEvent } from 'react';
import React, { FC, ChangeEvent, memo } from 'react';
import styled from '@emotion/styled';

export interface LevelProps {
Expand All @@ -16,15 +16,17 @@ export interface LevelProps {
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
}

export const Level: FC<LevelProps> = ({ children, value, onChange }) => (
export const Level: FC<LevelProps> = memo(({ children, value, onChange }) => (
<Select onChange={onChange} value={value}>
{children.map((item: string) => (
<Option key={item} value={item}>
{item}
</Option>
))}
</Select>
);
));

Level.displayName = 'Level';

const Select = styled.select`
margin: 0;
Expand Down
8 changes: 5 additions & 3 deletions src/components/Scoreboard/Reset.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, memo } from 'react';
import styled from '@emotion/styled';

import { useMouseDown } from '@/components/hooks/useMouseDown';
Expand All @@ -10,7 +10,7 @@ export interface ResetProps {
onReset: () => void;
}

export const Reset: FC<ResetProps> = ({ onReset }) => {
export const Reset: FC<ResetProps> = memo(({ onReset }) => {
const [mouseDown, onMouseDown, onMouseUp] = useMouseDown();

return (
Expand All @@ -23,7 +23,9 @@ export const Reset: FC<ResetProps> = ({ onReset }) => {
{mouseDown ? '😯' : '🙂'}
</Button>
);
};
});

Reset.displayName = 'Reset';

const Button = styled.button`
font-size: 1.1vw;
Expand Down
16 changes: 10 additions & 6 deletions src/components/Top/Top.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import React, { FC } from 'react';
import React, { FC, memo } from 'react';
import styled from '@emotion/styled';

import { Legend, LegendProps } from './Legend';
import { GameName, GameNameProps } from './GameName';

export type TopComponentType = LegendProps & GameNameProps;

export const Top: FC<TopComponentType> = ({ children, ...legendProps }) => (
<Header>
<GameName>{children}</GameName>
<Legend {...legendProps} />
</Header>
export const Top: FC<TopComponentType> = memo(
({ children, ...legendProps }) => (
<Header>
<GameName>{children}</GameName>
<Legend {...legendProps} />
</Header>
)
);

Top.displayName = 'Top';

const Header = styled.header`
text-align: center;
position: relative;
Expand Down
14 changes: 8 additions & 6 deletions src/modules/GameWithHooks/GameWithHooks.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, useCallback } from 'react';

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

Expand Down Expand Up @@ -26,6 +26,12 @@ export const GameWithHooks: FC = () => {

const [, bombs] = settings;

const onChangeLevelHandler = useCallback(
({ target: { value: level } }: React.ChangeEvent<HTMLSelectElement>) =>
onChangeLevel(level as LevelNames),
[]
);

return (
<Wrapper>
<Top feature="Flag" firstAction="right click">
Expand All @@ -37,11 +43,7 @@ export const GameWithHooks: FC = () => {
bombs={String(bombs - flagCounter)}
levels={GameLevels as unknown as string[]}
defaultLevel={level}
onChangeLevel={({
target: { value: level },
}: React.ChangeEvent<HTMLSelectElement>) =>
onChangeLevel(level as LevelNames)
}
onChangeLevel={onChangeLevelHandler}
onReset={onReset}
/>
{isGameOver && <GameOver onClick={onReset} isWin={isWin} />}
Expand Down
66 changes: 36 additions & 30 deletions src/modules/GameWithHooks/useGame.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useCallback } from 'react';

import {
Field,
Expand Down Expand Up @@ -61,39 +61,45 @@ export const useGame = (): ReturnType => {
fieldGenerator(size, bombs / (size * size))
);

const onClick = (coords: Coords) => {
!isGameStarted && setInProgress();
try {
const [newPlayerField, isSolved] = openCell(
const onClick = useCallback(
(coords: Coords) => {
!isGameStarted && setInProgress();
try {
const [newPlayerField, isSolved] = openCell(
coords,
playerField,
gameField
);
if (isSolved) {
setGameWin();
}
setPlayerField([...newPlayerField]);
} catch (e) {
setPlayerField([...gameField]);
setGameLoose();
}
},
[isGameStarted, isGameOver, isWin, level, flagCounter]
);

const onContextMenu = useCallback(
(coords: Coords) => {
!isGameStarted && setInProgress();
const [newPlayerField, isSolved, newFlagCounter] = setFlag(
coords,
playerField,
gameField
gameField,
flagCounter,
bombs
);
setFlagCounter(newFlagCounter);
if (isSolved) {
setGameWin();
}
setPlayerField([...newPlayerField]);
} catch (e) {
setPlayerField([...gameField]);
setGameLoose();
}
};

const onContextMenu = (coords: Coords) => {
!isGameStarted && setInProgress();
const [newPlayerField, isSolved, newFlagCounter] = setFlag(
coords,
playerField,
gameField,
flagCounter,
bombs
);
setFlagCounter(newFlagCounter);
if (isSolved) {
setGameWin();
}
setPlayerField([...newPlayerField]);
};
},
[isGameStarted, isGameOver, isWin, level, flagCounter]
);

const resetHandler = ([size, bombs]: [number, number]) => {
const newGameField = fieldGenerator(size, bombs / (size * size));
Expand All @@ -109,12 +115,12 @@ export const useGame = (): ReturnType => {
setFlagCounter(0);
};

const onChangeLevel = (level: LevelNames) => {
const onChangeLevel = useCallback((level: LevelNames) => {
const newSettings = setLevel(level);
resetHandler(newSettings);
};
}, []);

const onReset = () => resetHandler([size, bombs]);
const onReset = useCallback(() => resetHandler([size, bombs]), [size, bombs]);

return {
level,
Expand Down

0 comments on commit 9b6889c

Please sign in to comment.