From 89a4f1b59407f755c4bc05122d20f92f85bd9a78 Mon Sep 17 00:00:00 2001 From: Robert Tipton Date: Sat, 11 Jun 2022 18:56:18 -0400 Subject: [PATCH 1/3] Added demo application. Co-authored-by: Chris LeBrett <100822842+fscgolden@users.noreply.github.com> Co-authored-by: Robby Tipton <100332566+RobbyTipton@users.noreply.github.com> Co-authored-by: Joseph Park <10874783+joeepark@users.noreply.github.com> Co-authored-by: David Kim <101825171+codejunkie7@users.noreply.github.com> Co-authored-by: Kevin HoEun Lee <14950124+khobread@users.noreply.github.com> --- demo-app/.babelrc | 7 + demo-app/.gitignore | 1 + demo-app/package.json | 46 +++++ demo-app/src/client/Components/Board.tsx | 174 +++++++++++++++++++ demo-app/src/client/Components/Box.tsx | 22 +++ demo-app/src/client/Components/Buttons.tsx | 19 ++ demo-app/src/client/Components/Home.tsx | 29 ++++ demo-app/src/client/Components/Increment.tsx | 13 ++ demo-app/src/client/Components/Nav.tsx | 14 ++ demo-app/src/client/Components/Row.tsx | 28 +++ demo-app/src/client/Router.tsx | 19 ++ demo-app/src/client/index.html | 10 ++ demo-app/src/client/style.css | 149 ++++++++++++++++ demo-app/src/types.ts | 11 ++ demo-app/tsconfig.json | 10 ++ demo-app/webpack.config.js | 53 ++++++ 16 files changed, 605 insertions(+) create mode 100644 demo-app/.babelrc create mode 100644 demo-app/.gitignore create mode 100644 demo-app/package.json create mode 100644 demo-app/src/client/Components/Board.tsx create mode 100644 demo-app/src/client/Components/Box.tsx create mode 100644 demo-app/src/client/Components/Buttons.tsx create mode 100644 demo-app/src/client/Components/Home.tsx create mode 100644 demo-app/src/client/Components/Increment.tsx create mode 100644 demo-app/src/client/Components/Nav.tsx create mode 100644 demo-app/src/client/Components/Row.tsx create mode 100644 demo-app/src/client/Router.tsx create mode 100644 demo-app/src/client/index.html create mode 100644 demo-app/src/client/style.css create mode 100644 demo-app/src/types.ts create mode 100644 demo-app/tsconfig.json create mode 100644 demo-app/webpack.config.js diff --git a/demo-app/.babelrc b/demo-app/.babelrc new file mode 100644 index 000000000..a45d619c1 --- /dev/null +++ b/demo-app/.babelrc @@ -0,0 +1,7 @@ + +{ + "presets": [ + "@babel/preset-env", + "@babel/preset-react" + ] +} \ No newline at end of file diff --git a/demo-app/.gitignore b/demo-app/.gitignore new file mode 100644 index 000000000..40b878db5 --- /dev/null +++ b/demo-app/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/demo-app/package.json b/demo-app/package.json new file mode 100644 index 000000000..dce9c6a02 --- /dev/null +++ b/demo-app/package.json @@ -0,0 +1,46 @@ +{ + "name": "typescript-module", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "nodemon ./src/server/server.ts & webpack-dev-server", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/CodesmithLLC/typescript-module.git" + }, + "author": "Allison Pratt (allison@codesmith.io)", + "license": "ISC", + "bugs": { + "url": "https://github.com/CodesmithLLC/typescript-module/issues" + }, + "homepage": "https://github.com/CodesmithLLC/typescript-module#readme", + "devDependencies": { + "@babel/core": "^7.16.7", + "@babel/preset-env": "^7.16.7", + "@babel/preset-react": "^7.16.7", + "@types/express": "^4.17.13", + "@types/node": "^17.0.8", + "@types/react": "^17.0.38", + "@types/react-dom": "^17.0.11", + "babel-loader": "^8.2.3", + "copy-webpack-plugin": "^10.2.0", + "css-loader": "^6.5.1", + "html-webpack-plugin": "^5.5.0", + "nodemon": "^2.0.15", + "ts-loader": "^9.2.6", + "typescript": "^4.5.4", + "webpack": "^5.65.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "^4.7.2" + }, + "dependencies": { + "express": "^4.17.2", + "react": "^18.1.0", + "react-dom": "^18.1.0", + "react-router-dom": "^6.3.0", + "ts-node": "^10.4.0" + } +} diff --git a/demo-app/src/client/Components/Board.tsx b/demo-app/src/client/Components/Board.tsx new file mode 100644 index 000000000..e7ce106ab --- /dev/null +++ b/demo-app/src/client/Components/Board.tsx @@ -0,0 +1,174 @@ +import React, { Component } from 'react'; +import Row from './Row'; +import { BoardText, BoardContent, Scoreboard, Player } from './../../types'; + +type BoardState = { + board: BoardContent; + currentPlayer: Player; + gameOver: boolean; + message: string; + scoreboard: Scoreboard; +}; + +class Board extends Component<{}, BoardState> { + constructor(props: any) { + super(props); + this.state = { + board: this.newBoard(), + currentPlayer: 'X', + gameOver: false, + message: '', + scoreboard: { X: 0, O: 0 }, + }; + + // these methods need to be bound to the context of `this` since + // these will be passed into event handlers and called from outside the object + // where the context of `this` would be otherwise different + this.resetBoard = this.resetBoard.bind(this); + this.handleBoxClick = this.handleBoxClick.bind(this); + } + + componentDidMount() { + this.getScores(); + } + + componentDidUpdate() { + this.checkForWinner(); + } + + /** + * @method newBoard + * @description - returns a blank BoardContent array, + * for the start of a new game + */ + newBoard(): BoardContent { + return [ + ['-', '-', '-'], + ['-', '-', '-'], + ['-', '-', '-'], + ]; + } + + /** + * @method resetBoard + * @description - sets to board object to be all '-', + * and sets gameOver and message to default state + */ + resetBoard(): void { + this.setState({ + gameOver: false, + board: this.newBoard(), + message: '', + }); + } + + /** + * @method checkForWinner + * @description - checks to see if either player has filled a row + * if so, ends the game and updates the message to declare winner + */ + checkForWinner(): void { + const { board, gameOver, currentPlayer } = this.state; + + // helper function to check if board is filled + const spacesLeft = (): boolean => { + for (let i of board) { + if (i.includes('-')) return true; + } + return false; + }; + + if (!gameOver) { + // win conditions: matching rows, columns, or diagonals, that are not empty('-') + if ( + (board[0][0] === board[0][1] && + board[0][1] === board[0][2] && + board[0][2] !== '-') || + (board[1][0] === board[1][1] && + board[1][1] === board[1][2] && + board[1][2] !== '-') || + (board[2][0] === board[2][1] && + board[2][1] === board[2][2] && + board[2][2] !== '-') || + (board[0][0] === board[1][0] && + board[1][0] === board[2][0] && + board[2][0] !== '-') || + (board[0][1] === board[1][1] && + board[1][1] === board[2][1] && + board[2][1] !== '-') || + (board[0][2] === board[1][2] && + board[1][2] === board[2][2] && + board[2][2] !== '-') || + (board[0][0] === board[1][1] && + board[1][1] === board[2][2] && + board[2][2] !== '-') || + (board[2][0] === board[1][1] && + board[1][1] === board[0][2] && + board[0][2] !== '-') + ) { + // winner is the person who's turn was previous + const winner: Player = currentPlayer === 'X' ? 'O' : 'X'; + + this.setState({ + gameOver: true, + message: `Player ${winner} wins!`, + }); + + this.getScores('POST', JSON.stringify({ winner })); + + // draw condition: no '-' remaining in board without above win condition triggering + } else if (!spacesLeft()) { + this.setState({ + gameOver: true, + message: 'Draw!', + }); + } + } + } + + getScores(method?: string, winner?: string) { + // If method is GET, send a GET request to api route to get scores. + // If method is POST, send a POST request to api route to post the scores. + } + + handleBoxClick(row: number, column: number): void { + const boardCopy: BoardContent = [ + [...this.state.board[0]], + [...this.state.board[1]], + [...this.state.board[2]], + ]; + boardCopy[row][column] = this.state.currentPlayer; + const newPlayer: Player = this.state.currentPlayer === 'X' ? 'O' : 'X'; + this.setState({ board: boardCopy, currentPlayer: newPlayer }); + } + + render() { + // insert logic to render rows here + const rows: Array = []; + for (let i = 0; i < 3; i++) { + rows.push( + + ); + } + // Destructure scores for X and O from state so that they can individually be rendered below + const { X, O }: Scoreboard = this.state.scoreboard; + + return ( +
+

Tic Tac Toe

+ {this.state.gameOver &&

{this.state.message}

} + {rows} + +
+ ); + } +} + +export default Board; diff --git a/demo-app/src/client/Components/Box.tsx b/demo-app/src/client/Components/Box.tsx new file mode 100644 index 000000000..53b79548d --- /dev/null +++ b/demo-app/src/client/Components/Box.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { BoardText } from '../../types'; + +type BoxProps = { + value: BoardText; + row: number; + column: number; + handleBoxClick: (row: number, column: number) => void; +}; + +const Box = (props: BoxProps) => { + return ( + + ); +}; + +export default Box; diff --git a/demo-app/src/client/Components/Buttons.tsx b/demo-app/src/client/Components/Buttons.tsx new file mode 100644 index 000000000..af0ffa7a8 --- /dev/null +++ b/demo-app/src/client/Components/Buttons.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import Increment from "./Increment"; + +function Buttons() { + const buttons = []; + for (let i = 0; i < 4; i++){ + buttons.push() + } + + return( +
+

Stateful Buttons

+

These buttons are functional components that each manage their own state with the useState hook.

+ {buttons} +
+ ) +} + +export default Buttons; \ No newline at end of file diff --git a/demo-app/src/client/Components/Home.tsx b/demo-app/src/client/Components/Home.tsx new file mode 100644 index 000000000..2f38d48c6 --- /dev/null +++ b/demo-app/src/client/Components/Home.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +function Home() { + return ( +
+

Lorem Ipsum

+

+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea + commodo consequat. Duis aute irure dolor in reprehenderit in voluptate + velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + occaecat cupidatat non proident, sunt in culpa qui officia deserunt + mollit anim id est laborum." +

+

+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea + commodo consequat. Duis aute irure dolor in reprehenderit in voluptate + velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + occaecat cupidatat non proident, sunt in culpa qui officia deserunt + mollit anim id est laborum." +

+
+ ); +} + +export default Home; diff --git a/demo-app/src/client/Components/Increment.tsx b/demo-app/src/client/Components/Increment.tsx new file mode 100644 index 000000000..73fc98650 --- /dev/null +++ b/demo-app/src/client/Components/Increment.tsx @@ -0,0 +1,13 @@ +import React, { useState } from 'react'; + +function Increment() { + const [count, setCount] = useState(0); + + return ( + + ); +} + +export default Increment; diff --git a/demo-app/src/client/Components/Nav.tsx b/demo-app/src/client/Components/Nav.tsx new file mode 100644 index 000000000..eb7c28842 --- /dev/null +++ b/demo-app/src/client/Components/Nav.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import { Link } from "react-router-dom"; + +function Nav() { + return( +
+ About + Tic-Tac-Toe + Counter +
+ ) +} + +export default Nav; \ No newline at end of file diff --git a/demo-app/src/client/Components/Row.tsx b/demo-app/src/client/Components/Row.tsx new file mode 100644 index 000000000..d9bf89ca6 --- /dev/null +++ b/demo-app/src/client/Components/Row.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import Box from './Box'; +import { BoardText } from '../../types'; + +type RowProps = { + handleBoxClick: (row: number, column: number) => void; + values: Array; + row: number; +}; + +const Row = (props: RowProps) => { + const boxes: Array = []; + for (let i = 0; i < 3; i++) { + boxes.push( + + ); + } + + return
{boxes}
; +}; + +export default Row; diff --git a/demo-app/src/client/Router.tsx b/demo-app/src/client/Router.tsx new file mode 100644 index 000000000..08a1e08ad --- /dev/null +++ b/demo-app/src/client/Router.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import Nav from "./Components/Nav"; +import Board from "./Components/Board"; +import Home from "./Components/Home"; +import Buttons from "./Components/Buttons"; + +const root = document.getElementById("root"); + +ReactDOM.render( + +