Skip to content
Open
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
1 change: 1 addition & 0 deletions _config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
theme: jekyll-theme-cayman
20 changes: 16 additions & 4 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,26 @@
}

.App-header {
background-color: #282c34;
min-height: 10vh;
display: flex;

flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
margin-bottom: 2em;
}
color: red;

margin-bottom: 1em;
}
h1 {
background: white;
border: 3px solid;
border-color: blue;
border-radius: 24px;
}
h2 {
color: rgb(0, 0, 0);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using named colors if the RGB vals exactly match one. For example, rgb(0, 0, 0) is the same as black.

/* background: white;
border: 4px solid;
border-radius: 12px; */
}
91 changes: 67 additions & 24 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useState } from 'react';
import './App.css';

import Board from './components/Board';
import matthew from './img/matthewface.png';
import junior from './img/juniorface.jpg';
Comment on lines +4 to +5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💜


const PLAYER_1 = 'X';
const PLAYER_2 = 'O';
const PLAYER_1 = <img src={matthew} alt="X" />;
const PLAYER_2 = <img src={junior} alt="O" />;
Comment on lines +7 to +8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, consider having further separation between data and presentation. Keeping the variables as X and O and changing only the rendering logic to show the kitties may be a bit cleaner. It also would have allowed you to still pass the automated tests.

let totalMoves = 0;

const generateSquares = () => {
const squares = [];
Expand All @@ -26,40 +28,81 @@ const generateSquares = () => {
};

const App = () => {
// This starts state off as a 2D array of JS objects with
// empty value and unique ids.
const resetCombos = () => {
return {
c048: [],
c012: [],
c345: [],
c678: [],
c036: [],
c147: [],
c258: [],
c642: [],
};
};
Comment on lines +32 to +42

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting method of tracking combos! In the future, consider adding a comment to explain how unconventional data structures like this are meant to be used so the code is easier to read.


const [squares, setSquares] = useState(generateSquares());
const [player, setPlayer] = useState(true);
const [winner, setWinner] = useState(null);
const [trackCombos, setTrackCombos] = useState(resetCombos());

const updateSquares = (id) => {
totalMoves++;
const updatedSquareData = squares.map((row) => {
return row.map((square) => {
if (square.id === id) {
if (player) {
square.value = PLAYER_1;
checkForWinner(square.id, square.value);
} else {
square.value = PLAYER_2;
checkForWinner(square.id, square.value);
}
}
return square;
});
});
Comment on lines +51 to +64

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice use of nested maps


setSquares(updatedSquareData);
setPlayer(!player);
};

// Wave 2
// You will need to create a method to change the square
// When it is clicked on.
// Then pass it into the squares as a callback
const checkForWinner = (id, value) => {
for (let key in trackCombos) {
if (key.includes(id)) {
trackCombos[key].push(value);

const checkForWinner = () => {
// Complete in Wave 3
// You will need to:
// 1. Go accross each row to see if
// 3 squares in the same row match
// i.e. same value
// 2. Go down each column to see if
// 3 squares in each column match
// 3. Go across each diagonal to see if
// all three squares have the same value.
if (trackCombos[key].length === 3) {
if (new Set(trackCombos[key]).size === 1) {
setWinner(value);
}
}
if (totalMoves === 9) {
setWinner('Tie!');
}
}
}
};

const resetGame = () => {
// Complete in Wave 4
setSquares(generateSquares());
setTrackCombos(resetCombos);
totalMoves = 0;
setPlayer(true);
setWinner(null);
};

return (
<div className="App">
<header className="App-header">
<h1>React Tic Tac Toe</h1>
<h2>The winner is ... -- Fill in for wave 3 </h2>
<button>Reset Game</button>
<h1>Tic Tac Toebeans</h1>
<h2>The winner is ... {winner}</h2>
<button className="button" onClick={resetGame}>
Start Over
</button>
</header>
<main>
<Board squares={squares} />
<Board squares={squares} updateSquares={updateSquares} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job passing down callback

</main>
</div>
);
Expand Down
19 changes: 10 additions & 9 deletions src/components/Board.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
div.grid {
width: 600px;
height: 600px;
margin: 0 auto;
background-color: #34495e;
color: #fff;
border: 6px solid #2c3e50;
border-radius: 10px;
display: grid;
grid-template: repeat(3, 1fr) / repeat(3, 1fr);
width: 500px;
height: 500px;
margin: 0 auto;
background-color: transparent;
color: #000000;

border: 3px solid #000000;
border-radius: 10px;
display: grid;
grid-template: repeat(3, 1fr) / repeat(3, 1fr);
Comment on lines +10 to +11

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😻

}
41 changes: 26 additions & 15 deletions src/components/Board.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,44 @@ import './Board.css';
import Square from './Square';
import PropTypes from 'prop-types';

const generateSquareComponents = (squares, updateSquares) => {
const singleArray = [];

const generateSquareComponents = (squares, onClickCallback) => {
// Complete this for Wave 1
// squares is a 2D Array, but
// you need to return a 1D array
// of square components
for (let subArray = 0; subArray < squares.length; subArray++) {
squares[subArray].forEach((square) => {
singleArray.push(square);
});
}

}
const squaresArray = singleArray.map((square) => {
return (
<Square
key={square.id}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job remembering key!

id={square.id}
value={square.value}
updateSquares={updateSquares}
></Square>
);
});
return squaresArray;
};

const Board = ({ squares, updateSquares }) => {
const squareList = generateSquareComponents(squares, updateSquares);

const Board = ({ squares, onClickCallback }) => {
const squareList = generateSquareComponents(squares, onClickCallback);
console.log(squareList);
return <div className="grid" >
{squareList}
</div>
}
return <div className="grid">{squareList}</div>;
};

Board.propTypes = {
squares: PropTypes.arrayOf(
PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
value: PropTypes.string.isRequired
value: PropTypes.any.isRequired,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job updating prop types! A more specific type you may have considered here is PropTypes.element.isRequired, as you're passing an img element.

})
)
),
onClickCallback: PropTypes.func.isRequired,
updateSquares: PropTypes.func.isRequired,
};

export default Board;
25 changes: 15 additions & 10 deletions src/components/Board.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import Board from './Board';
import { render, screen, fireEvent} from '@testing-library/react'
import { render, screen, fireEvent } from '@testing-library/react';

// Sample input to the Board component
const SAMPLE_BOARD = [
Expand Down Expand Up @@ -46,15 +46,14 @@ const SAMPLE_BOARD = [
value: 'X',
id: 8,
},
],
],
];

describe('Wave 1: Board', () => {

test('that board will render with the proper number of Xs and Os', () => {
// Act
render(<Board squares={SAMPLE_BOARD} onClickCallback={() => { }} />);
render(<Board squares={SAMPLE_BOARD} onClickCallback={() => {}} />);

// Assert
const xSquares = screen.getAllByText('X');
expect(xSquares.length).toEqual(5);
Expand All @@ -70,12 +69,14 @@ describe('Wave 1: Board', () => {
return {
...square,
value: '',
}
};
});
});

// Act
const { container } = render(<Board squares={emptyBoard} onClickCallback={() => { }} />);
const { container } = render(
<Board squares={emptyBoard} onClickCallback={() => {}} />
);

// Assert
const buttons = container.querySelectorAll('.grid button');
Expand All @@ -87,7 +88,9 @@ describe('Wave 2: Board', () => {
test.skip('that the callback is called for the 1st button', () => {
// Arrange
const callback = jest.fn();
const { container } = render(<Board squares={SAMPLE_BOARD} onClickCallback={callback} />);
const { container } = render(
<Board squares={SAMPLE_BOARD} onClickCallback={callback} />
);
const buttons = container.querySelectorAll('.grid button');

// Act
Expand All @@ -100,7 +103,9 @@ describe('Wave 2: Board', () => {
test.skip('that the callback is called for the last button', () => {
// Arrange
const callback = jest.fn();
const { container } = render(<Board squares={SAMPLE_BOARD} onClickCallback={callback} />);
const { container } = render(
<Board squares={SAMPLE_BOARD} onClickCallback={callback} />
);
const buttons = container.querySelectorAll('.grid button');

// Act
Expand All @@ -110,4 +115,4 @@ describe('Wave 2: Board', () => {
expect(callback).toHaveBeenCalled();
});
});
});
});
14 changes: 8 additions & 6 deletions src/components/Square.css
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
.square
{
border: 4px solid #2c3e50;
border-radius: 2px;
font-family: Helvetica;
@import url('https://fonts.googleapis.com/css2?family=Rock+3D&display=swap');

.square {
background-color: transparent;
border: 3px solid #000000;
border-radius: 4px;
font-family: 'Rock 3D';
font-weight: bold;
font-size: 8em;
display: flex;
justify-content: center;
align-items: center;
margin: 2px;
}
}
30 changes: 17 additions & 13 deletions src/components/Square.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import './Square.css';

import './Square.css'

const Square = (props) => {
// For Wave 1 enable this
// Component to alert a parent
const Square = ({ id, value, updateSquares }) => {
// For Wave 1 enable this
// Component to alert a parent
// component when it's clicked on.

return <button
className="square"
>
{props.value}
</button>
}
return (
<button
className="square"
onClick={() => {
updateSquares(id);
}}
Comment on lines +13 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice use of anonymous function.

>
{value}
</button>
);
};

Square.propTypes = {
value: PropTypes.string.isRequired,
onClickCallback: PropTypes.func.isRequired,
value: PropTypes.any.isRequired,
updateSquares: PropTypes.func.isRequired,
id: PropTypes.number.isRequired,
};

Expand Down
Binary file added src/img/blue_cat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/img/juniorface.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/img/matthewface.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading