diff --git a/src/index.tsx b/src/index.tsx index e69de29..36580fd 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -0,0 +1,2 @@ +import React from 'react'; +import { render } from 'react-dom'; diff --git a/src/lesson4/Cell.css b/src/lesson4/Cell.css new file mode 100644 index 0000000..b239643 --- /dev/null +++ b/src/lesson4/Cell.css @@ -0,0 +1,23 @@ +.cell { + background: #fff; + border: 1px solid #999; + font-size: 24px; + font-weight: bold; + line-height: 34px; + height: 34px; + margin-right: -1px; + margin-top: -1px; + padding: 0; + text-align: center; + width: 34px; + display: inline-block; +} + +.cell-empty { + border-color: gray; +} + +.cell-filled { + border-color: #fff; + background: black; +} diff --git a/src/lesson4/Cell.stories.tsx b/src/lesson4/Cell.stories.tsx new file mode 100644 index 0000000..35a4394 --- /dev/null +++ b/src/lesson4/Cell.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Cell } from './Cell'; +import { withKnobs, number } from '@storybook/addon-knobs'; + +// eslint-disable-next-line no-restricted-syntax +export default { + title: 'Lesson 4 / Cell', + decorators: [withKnobs], +}; + +export const nonFilled = () => [ + , +]; + +export const Filled = () => [ + , +]; diff --git a/src/lesson4/Cell.test.tsx b/src/lesson4/Cell.test.tsx new file mode 100644 index 0000000..4ced06b --- /dev/null +++ b/src/lesson4/Cell.test.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import { Cell } from './Cell'; + +test.each` + inputFilled | expected + ${true} | ${'
'} + ${false} | ${'
'} +`('renders cell for empty or filled', ({ inputFilled, expected }) => { + const cellItem = mount(); + + expect(cellItem.html()).toBe(expected); +}); diff --git a/src/lesson4/Cell.tsx b/src/lesson4/Cell.tsx new file mode 100644 index 0000000..6c3a4c3 --- /dev/null +++ b/src/lesson4/Cell.tsx @@ -0,0 +1,13 @@ +import React, { FC } from 'react'; +import './Cell.css'; +import type { CellProps } from './Interfaces'; + +const Cell: FC = ({ filled, x, y }) => { + if (filled) { + return
{filled}
; + } + + return
; +}; + +export { Cell }; diff --git a/src/lesson4/Field.css b/src/lesson4/Field.css new file mode 100644 index 0000000..99705aa --- /dev/null +++ b/src/lesson4/Field.css @@ -0,0 +1,10 @@ +p { + margin: 0; + padding: 0; + line-height: 0; +} +div { + margin: 0; + padding: 0; + line-height: 0; +} diff --git a/src/lesson4/Field.stories.tsx b/src/lesson4/Field.stories.tsx new file mode 100644 index 0000000..c437d62 --- /dev/null +++ b/src/lesson4/Field.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Field } from './Field'; +import { withKnobs, object } from '@storybook/addon-knobs'; + +// eslint-disable-next-line no-restricted-syntax +export default { + title: 'Lesson 4 / Field', + decorators: [withKnobs], +}; + +const cellGridFillRandom = (cellStatus = () => Math.random() < 0.3) => { + const grid: boolean[][] = []; + for (let y = 0; y < 10; y++) { + grid[y] = []; + for (let x = 0; x < 10; x++) { + grid[y][x] = cellStatus(); + } + } + return grid; +}; + +const cellGridNonFill = () => + Array.from({ length: 10 }).fill( + Array.from({ length: 10 }).fill(false), + ); + +export const emptyCell = () => [ + , +]; + +export const notEmptyCell = () => [ + , +]; diff --git a/src/lesson4/Field.test.tsx b/src/lesson4/Field.test.tsx new file mode 100644 index 0000000..22845e8 --- /dev/null +++ b/src/lesson4/Field.test.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import { Field } from './Field'; + +test.each` + inputFilled | expectedBr | expectedDiv | expectedCellEmpty | expectedCellFilled + ${[ + [true, false, true, true, false, true, true, true, false, true], + [true, true, true, true, false, false, false, true, false, true], +]} | ${2} | ${20} | ${7} | ${13} +`( + 'testing rendering field', + ({ + inputFilled, + expectedBr, + expectedDiv, + expectedCellEmpty, + expectedCellFilled, + }) => { + const field = mount(); + expect(field.find('p').length).toBe(expectedBr); + expect(field.find('.cell').length).toBe(expectedDiv); + expect(field.find('.cell-empty').length).toBe(expectedCellEmpty); + expect(field.find('.cell-filled').length).toBe(expectedCellFilled); + }, +); diff --git a/src/lesson4/Field.tsx b/src/lesson4/Field.tsx new file mode 100644 index 0000000..8d35853 --- /dev/null +++ b/src/lesson4/Field.tsx @@ -0,0 +1,17 @@ +import React, { FC } from 'react'; +import { Cell } from './Cell'; +import './Field.css'; +import type { FieldProps } from './Interfaces'; + +const Field: FC = ({ field }) => ( +
+ {field.map((row, y) => [ + ...row.map((filled: boolean, x) => ( + + )), + y !== row.length - 1 ?

: null, + ])} +

+); + +export { Field }; diff --git a/src/lesson4/GameOfLifeProto.css b/src/lesson4/GameOfLifeProto.css new file mode 100644 index 0000000..9a2646e --- /dev/null +++ b/src/lesson4/GameOfLifeProto.css @@ -0,0 +1,3 @@ +.btn { + margin: 20px 0; +} diff --git a/src/lesson4/GameOfLifeProto.stories.tsx b/src/lesson4/GameOfLifeProto.stories.tsx new file mode 100644 index 0000000..f9e8f1b --- /dev/null +++ b/src/lesson4/GameOfLifeProto.stories.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { withKnobs, number } from '@storybook/addon-knobs'; +import { Field } from './Field'; +import { GameOfLifeProto } from './GameOfLifeProto'; + +// eslint-disable-next-line no-restricted-syntax +export default { + title: 'Lesson 4 / GameOfLifeProto', + decorators: [withKnobs], +}; + +export const GameOfLifePrototip = () => ( + +); diff --git a/src/lesson4/GameOfLifeProto.test.tsx b/src/lesson4/GameOfLifeProto.test.tsx new file mode 100644 index 0000000..46bbd1b --- /dev/null +++ b/src/lesson4/GameOfLifeProto.test.tsx @@ -0,0 +1,5 @@ +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import GameOfLifeProto from './GameOfLifeProto'; + +describe('GameOfLifeComponent testing', () => {}); diff --git a/src/lesson4/GameOfLifeProto.tsx b/src/lesson4/GameOfLifeProto.tsx new file mode 100644 index 0000000..0281061 --- /dev/null +++ b/src/lesson4/GameOfLifeProto.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import './GameOfLifeProto.css'; + +type FieldPropsComponent = React.FC<{ + field: boolean[][]; +}>; + +interface FieldPropsInterface { + rows: number; + columns: number; + fieldComponent: FieldPropsComponent; +} + +interface FieldState { + fieldState: boolean[][]; +} + +const cellGridFillRandom = ( + rows: number, + columns: number, + cellStatus = () => Math.random() < 0.3, +) => { + const grid: boolean[][] = []; + for (let y = 0; y < rows; y++) { + grid[y] = []; + for (let x = 0; x < columns; x++) { + grid[y][x] = cellStatus(); + } + } + return grid; +}; + +class GameOfLifeProto extends React.Component { + private fieldComponent: FieldPropsComponent; + + constructor(props: FieldPropsInterface) { + super(props); + this.fieldComponent = props.fieldComponent; + this.state = { + fieldState: cellGridFillRandom(this.props.rows, this.props.columns), + }; + } + + public onClick() { + this.setState(() => { + const cloneFieldState = cellGridFillRandom( + this.props.rows, + this.props.columns, + ); + + return { + fieldState: cloneFieldState, + }; + }); + } + + render() { + const FieldComponent = this.fieldComponent; + return ( + <> + + + + ); + } +} + +export { GameOfLifeProto }; diff --git a/src/lesson4/Interfaces.ts b/src/lesson4/Interfaces.ts new file mode 100644 index 0000000..6ecc75c --- /dev/null +++ b/src/lesson4/Interfaces.ts @@ -0,0 +1,9 @@ +export interface CellProps { + filled?: boolean; + x?: number; + y?: number; +} + +export interface FieldProps { + field: boolean[][]; +} diff --git a/tsconfig.json b/tsconfig.json index 4ef2a6d..40d51d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ "allowJs": true /* Allow javascript files to be compiled. */,