From a33ebeccddcdd81c75533422c4bddcf5d03f1ca8 Mon Sep 17 00:00:00 2001 From: Wenszel Date: Wed, 13 Dec 2023 16:31:52 +0100 Subject: [PATCH] added tests and css modules to LoginPage components --- backend/handlers/roomHandler.js | 4 +- src/components/HOC/withLoading.jsx | 14 +++ .../LoginPage/AddServer/AddServer.css | 35 -------- .../LoginPage/AddServer/AddServer.jsx | 62 ++++++------- .../LoginPage/AddServer/AddServer.module.css | 15 ++++ .../LoginPage/AddServer/AddServer.test.js | 82 +++++++++++++++++ .../LoginPage/JoinServer/JoinServer.jsx | 66 ++++++++++++++ .../JoinServer/JoinServer.module.css | 20 +++++ .../JoinServer/ServersTable/ServersTable.jsx | 33 +++++++ .../ServersTable/ServersTable.module.css | 40 +++++++++ .../ServersTable/ServersTable.test.js | 34 +++++++ src/components/LoginPage/LoginPage.jsx | 15 ++-- src/components/LoginPage/LoginPage.module.css | 8 ++ src/components/LoginPage/LoginPage.test.js | 19 ++++ .../LoginPage/NameInput/NameInput.css | 59 ------------ .../LoginPage/NameInput/NameInput.jsx | 13 +-- .../LoginPage/NameInput/NameInput.module.css | 20 +++++ .../LoginPage/NameInput/NameInput.test.js | 89 +++++++++++++++++++ .../LoginPage/ServerList/ServerList.css | 51 ----------- .../LoginPage/ServerList/ServerList.jsx | 82 ----------------- .../LoginPage/WindowLayout/WindowLayout.jsx | 15 ++++ .../WindowLayout.module.css} | 25 ++---- .../WindowLayout/WindowLayout.test.js | 28 ++++++ src/hooks/useInput.js | 4 +- src/hooks/useKeyPress.js | 2 +- src/hooks/useSocketData.js | 10 ++- src/index.css | 67 ++++++++++++++ 27 files changed, 614 insertions(+), 298 deletions(-) create mode 100644 src/components/HOC/withLoading.jsx delete mode 100644 src/components/LoginPage/AddServer/AddServer.css create mode 100644 src/components/LoginPage/AddServer/AddServer.module.css create mode 100644 src/components/LoginPage/AddServer/AddServer.test.js create mode 100644 src/components/LoginPage/JoinServer/JoinServer.jsx create mode 100644 src/components/LoginPage/JoinServer/JoinServer.module.css create mode 100644 src/components/LoginPage/JoinServer/ServersTable/ServersTable.jsx create mode 100644 src/components/LoginPage/JoinServer/ServersTable/ServersTable.module.css create mode 100644 src/components/LoginPage/JoinServer/ServersTable/ServersTable.test.js create mode 100644 src/components/LoginPage/LoginPage.module.css create mode 100644 src/components/LoginPage/LoginPage.test.js delete mode 100644 src/components/LoginPage/NameInput/NameInput.css create mode 100644 src/components/LoginPage/NameInput/NameInput.module.css create mode 100644 src/components/LoginPage/NameInput/NameInput.test.js delete mode 100644 src/components/LoginPage/ServerList/ServerList.css delete mode 100644 src/components/LoginPage/ServerList/ServerList.jsx create mode 100644 src/components/LoginPage/WindowLayout/WindowLayout.jsx rename src/components/LoginPage/{LoginPage.css => WindowLayout/WindowLayout.module.css} (76%) create mode 100644 src/components/LoginPage/WindowLayout/WindowLayout.test.js diff --git a/backend/handlers/roomHandler.js b/backend/handlers/roomHandler.js index f99324c..d01b908 100644 --- a/backend/handlers/roomHandler.js +++ b/backend/handlers/roomHandler.js @@ -16,13 +16,13 @@ module.exports = socket => { }; const handleGetAllRooms = async () => { - let rooms = await getRooms(); + const rooms = await getRooms(); sendToOnePlayerRooms(socket.id, rooms); }; const handleCreateRoom = async data => { createNewRoom(data); - socket.to(socket.id).emit('room:created'); + sendToOnePlayerRooms(socket.id, await getRooms()); }; socket.on('room:data', handleGetData); diff --git a/src/components/HOC/withLoading.jsx b/src/components/HOC/withLoading.jsx new file mode 100644 index 0000000..d68ecd3 --- /dev/null +++ b/src/components/HOC/withLoading.jsx @@ -0,0 +1,14 @@ +import ReactLoading from 'react-loading'; + +const withLoading = Component => { + return function WithLoading({ isLoading, ...props }) { + if (!isLoading) { + return ; + } + return ( + + ); + }; +}; + +export default withLoading; diff --git a/src/components/LoginPage/AddServer/AddServer.css b/src/components/LoginPage/AddServer/AddServer.css deleted file mode 100644 index 4c64921..0000000 --- a/src/components/LoginPage/AddServer/AddServer.css +++ /dev/null @@ -1,35 +0,0 @@ -.refresh { - display: flex; - margin-left: auto; - justify-content: center; - align-items: center; - width: 40px; - height: 100%; - border: 1px solid white; -} -.refresh > img { - width: 20px; - height: 20px; - cursor: pointer; -} - -form { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100%; -} - -.private-container { - margin-left: 10px; - display: flex; - flex-direction: row; - align-items: center; - width: 100%; -} -input:disabled { - background-color: black; - color: #999; - border: 1px solid #ddd; -} diff --git a/src/components/LoginPage/AddServer/AddServer.jsx b/src/components/LoginPage/AddServer/AddServer.jsx index a02c5e1..e065623 100644 --- a/src/components/LoginPage/AddServer/AddServer.jsx +++ b/src/components/LoginPage/AddServer/AddServer.jsx @@ -1,56 +1,50 @@ -import React, { useState, useContext, useEffect } from 'react'; -import './AddServer.css'; +import React, { useState, useContext } from 'react'; import Switch from '@mui/material/Switch'; import { SocketContext } from '../../../App'; +import WindowLayout from '../WindowLayout/WindowLayout'; +import useInput from '../../../hooks/useInput'; +import styles from './AddServer.module.css'; + const AddServer = () => { const socket = useContext(SocketContext); const [isPrivate, setIsPrivate] = useState(false); - const [serverName, setServerName] = useState(''); - const [password, setPassword] = useState(''); - - useEffect(() => { - socket.on('room:created', () => { - socket.emit('room:rooms'); - }); - }, [socket]); + const [isIncorrect, setIsIncorrect] = useState(false); + const serverName = useInput(''); + const password = useInput(''); const handleButtonClick = e => { e.preventDefault(); - socket.emit('room:create', { - name: serverName, - private: isPrivate, - password: password, - }); + if (!serverName.value) setIsIncorrect(true); + else + socket.emit('room:create', { + name: serverName.value, + password: password.value, + private: isPrivate, + }); }; return ( -
-
-

Host A Server

-
-
-
+ setServerName(e.target.value)} placeholder='Server Name' + {...serverName} + style={{ + border: isIncorrect ? '1px solid red' : '1px solid white', + }} /> -
-

Private

+
+ setIsPrivate(!isPrivate)} />
- setPassword(e.target.value)} - placeholder='password' - disabled={!isPrivate} - /> + -
-
+ } + /> ); }; diff --git a/src/components/LoginPage/AddServer/AddServer.module.css b/src/components/LoginPage/AddServer/AddServer.module.css new file mode 100644 index 0000000..7c91a94 --- /dev/null +++ b/src/components/LoginPage/AddServer/AddServer.module.css @@ -0,0 +1,15 @@ +.formContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; +} + +.privateContainer { + margin-left: 10px; + display: flex; + flex-direction: row; + align-items: center; + width: 100%; +} diff --git a/src/components/LoginPage/AddServer/AddServer.test.js b/src/components/LoginPage/AddServer/AddServer.test.js new file mode 100644 index 0000000..3602cb4 --- /dev/null +++ b/src/components/LoginPage/AddServer/AddServer.test.js @@ -0,0 +1,82 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { SocketContext } from '../../../App'; +import AddServer from './AddServer'; + +const mockSocket = { + emit: jest.fn(), +}; + +describe('AddServer component', () => { + it('should renders without crashing', () => { + render( + + + + ); + expect(screen.getByText('Host A Server')).toBeInTheDocument(); + }); + + it('should handles form submission with valid data when private', () => { + render( + + + + ); + + const serverNameInput = screen.getByPlaceholderText('Server Name'); + fireEvent.change(serverNameInput, { target: { value: 'Test Server' } }); + + const privateSwitch = screen.getByRole('checkbox'); + fireEvent.click(privateSwitch); + + const passwordInput = screen.getByPlaceholderText('password'); + fireEvent.change(passwordInput, { target: { value: 'TestPassword' } }); + + const hostButton = screen.getByText('Host'); + fireEvent.click(hostButton); + + expect(mockSocket.emit).toHaveBeenCalledWith('room:create', { + name: 'Test Server', + password: 'TestPassword', + private: true, + }); + }); + + it('should handles form submission with valid data when not private', () => { + render( + + + + ); + + const serverNameInput = screen.getByPlaceholderText('Server Name'); + fireEvent.change(serverNameInput, { target: { value: 'Test Server' } }); + + const hostButton = screen.getByText('Host'); + fireEvent.click(hostButton); + + expect(mockSocket.emit).toHaveBeenCalledWith('room:create', { + name: 'Test Server', + password: '', + private: false, + }); + }); + + it('should handles form submission with missing server name', () => { + render( + + + + ); + + const hostButton = screen.getByText('Host'); + fireEvent.click(hostButton); + + expect(mockSocket.emit).not.toHaveBeenCalled(); + + const serverNameInput = screen.getByPlaceholderText('Server Name'); + expect(serverNameInput).toHaveStyle('border: 1px solid red'); + }); +}); diff --git a/src/components/LoginPage/JoinServer/JoinServer.jsx b/src/components/LoginPage/JoinServer/JoinServer.jsx new file mode 100644 index 0000000..c267007 --- /dev/null +++ b/src/components/LoginPage/JoinServer/JoinServer.jsx @@ -0,0 +1,66 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { SocketContext } from '../../../App'; +import refresh from '../../../images/login-page/refresh.png'; +import NameInput from '../NameInput/NameInput'; +import Overlay from '../../Overlay/Overlay'; +import WindowLayout from '../WindowLayout/WindowLayout'; +import ServersTable from './ServersTable/ServersTable'; +import withLoading from '../../HOC/withLoading'; +import useSocketData from '../../../hooks/useSocketData'; +import styles from './JoinServer.module.css'; + +const JoinServer = () => { + const socket = useContext(SocketContext); + const [rooms, setRooms] = useSocketData('room:rooms'); + + const [joining, setJoining] = useState(false); + const [clickedRoom, setClickedRoom] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + socket.emit('room:rooms'); + socket.on('room:rooms', () => { + setIsLoading(false); + }); + }, [socket]); + + const getRooms = () => { + setRooms([]); + socket.emit('room:rooms'); + }; + + const handleJoinClick = room => { + setClickedRoom(room); + setJoining(true); + }; + + const ServersTableWithLoading = withLoading(ServersTable); + + return ( + <> + + refresh +
+ } + content={ +
+ +
+ } + /> + {joining ? ( + setJoining(false)}> + + + ) : null} + + ); +}; +export default JoinServer; diff --git a/src/components/LoginPage/JoinServer/JoinServer.module.css b/src/components/LoginPage/JoinServer/JoinServer.module.css new file mode 100644 index 0000000..bd89d03 --- /dev/null +++ b/src/components/LoginPage/JoinServer/JoinServer.module.css @@ -0,0 +1,20 @@ +.serversTableContainer { + display: flex; + height: 500px; + overflow: scroll; + width: 100%; +} +.refresh { + display: flex; + margin-left: auto; + justify-content: center; + align-items: center; + width: 40px; + height: 100%; + border: 1px solid white; +} +.refresh > img { + width: 20px; + height: 20px; + cursor: pointer; +} diff --git a/src/components/LoginPage/JoinServer/ServersTable/ServersTable.jsx b/src/components/LoginPage/JoinServer/ServersTable/ServersTable.jsx new file mode 100644 index 0000000..d6b9987 --- /dev/null +++ b/src/components/LoginPage/JoinServer/ServersTable/ServersTable.jsx @@ -0,0 +1,33 @@ +import lock from '../../../../images/login-page/lock.png'; +import styles from './ServersTable.module.css'; + +const ServerListTable = ({ rooms, handleJoinClick }) => { + return ( + + + + + + + + + + + + {rooms.map((room, index) => ( + + + + + + + + ))} + +
Server#/#Status
{room.private ? private : null}{room.name}{`${room.players.length}/4`}{room.isStarted ? 'started' : 'waiting'} + +
+ ); +}; + +export default ServerListTable; diff --git a/src/components/LoginPage/JoinServer/ServersTable/ServersTable.module.css b/src/components/LoginPage/JoinServer/ServersTable/ServersTable.module.css new file mode 100644 index 0000000..25636ad --- /dev/null +++ b/src/components/LoginPage/JoinServer/ServersTable/ServersTable.module.css @@ -0,0 +1,40 @@ +.roomName { + max-width: 150px; + overflow: hidden; + text-align: left !important; +} +.rooms > thead > tr :nth-child(2) { + text-align: left; +} +.rooms > tbody > tr > td > img { + margin-right: 5px; + width: 20px; + height: 20px; +} + +.rooms > th { + padding: 8px; + text-align: center; + height: 50px; +} +.rooms > tbody > tr > td { + padding: 4px; + text-align: center; + height: 50px; +} +.rooms > tbody > tr > td { + max-height: 50px; + height: 10px; +} + +.rooms { + border-collapse: collapse; + width: 100%; +} + +.lastColumn { + width: 70px; +} +.firstColumn { + width: 40px; +} diff --git a/src/components/LoginPage/JoinServer/ServersTable/ServersTable.test.js b/src/components/LoginPage/JoinServer/ServersTable/ServersTable.test.js new file mode 100644 index 0000000..6ecde47 --- /dev/null +++ b/src/components/LoginPage/JoinServer/ServersTable/ServersTable.test.js @@ -0,0 +1,34 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import ServersTable from './ServersTable'; + +const mockRooms = [ + { _id: '1', name: 'Room 1', private: false, players: [], isStarted: false }, + { _id: '2', name: 'Room 2', private: true, players: [], isStarted: true }, +]; + +describe('ServersTable component', () => { + it('should renders without crashing', () => { + render( {}} />); + expect(screen.getByText('Server')).toBeInTheDocument(); + expect(screen.getByText('#/#')).toBeInTheDocument(); + expect(screen.getByText('Status')).toBeInTheDocument(); + }); + + it('should renders the list of rooms', () => { + render( {}} />); + expect(screen.getByText('Room 1')).toBeInTheDocument(); + expect(screen.getByText('Room 2')).toBeInTheDocument(); + }); + + it('should handles join click for each room', () => { + const handleJoinClick = jest.fn(); + render(); + + const joinButtons = screen.getAllByText('Join'); + fireEvent.click(joinButtons[0]); + + expect(handleJoinClick).toHaveBeenCalledWith(mockRooms[0]); + }); +}); diff --git a/src/components/LoginPage/LoginPage.jsx b/src/components/LoginPage/LoginPage.jsx index 0a3f19f..2971fd6 100644 --- a/src/components/LoginPage/LoginPage.jsx +++ b/src/components/LoginPage/LoginPage.jsx @@ -1,14 +1,13 @@ -import './LoginPage.css'; import AddServer from './AddServer/AddServer'; -import ServerList from './ServerList/ServerList'; +import JoinServer from './JoinServer/JoinServer'; +import styles from './LoginPage.module.css'; + const LoginPage = () => { return ( - <> -
- - -
- +
+ + +
); }; diff --git a/src/components/LoginPage/LoginPage.module.css b/src/components/LoginPage/LoginPage.module.css new file mode 100644 index 0000000..b15dd68 --- /dev/null +++ b/src/components/LoginPage/LoginPage.module.css @@ -0,0 +1,8 @@ +.container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: flex-start; + height: 50%; + width: 100%; +} diff --git a/src/components/LoginPage/LoginPage.test.js b/src/components/LoginPage/LoginPage.test.js new file mode 100644 index 0000000..d165830 --- /dev/null +++ b/src/components/LoginPage/LoginPage.test.js @@ -0,0 +1,19 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import LoginPage from './LoginPage'; + +jest.mock('./JoinServer/JoinServer', () => () =>
); +jest.mock('./AddServer/AddServer', () => () =>
); + +describe('LoginPage component', () => { + it('should renders JoinServer component ', () => { + render(); + expect(screen.getByTestId('join-server')).toBeInTheDocument(); + }); + + it('should renders AddServer component', () => { + render(); + expect(screen.getByTestId('add-server')).toBeInTheDocument(); + }); +}); diff --git a/src/components/LoginPage/NameInput/NameInput.css b/src/components/LoginPage/NameInput/NameInput.css deleted file mode 100644 index 8e311e7..0000000 --- a/src/components/LoginPage/NameInput/NameInput.css +++ /dev/null @@ -1,59 +0,0 @@ -.name-input-container { - display: flex; - flex-direction: column; - padding: 10px 20px 60px 20px; - width: 300px; - background: radial-gradient(circle, rgba(0, 138, 255, 1) 5%, rgba(9, 9, 121, 1) 81%); - border: 1px solid white; - border-radius: 8px; - margin: 20px; - z-index: 2; -} -.name-input-container > button { - margin-top: 5px; - text-align: center; - width: 100px; - align-self: center; -} -.name-input-container > input { - margin-top: 10px; -} -input, -button { - padding: 0; - border: none; - outline: none; - box-sizing: border-box; -} - -input { - width: 100%; - padding: 12px; - font-size: 16px; - border-radius: 8px; - color: white; - border: 1px solid #ccc; - background-color: rgba(0, 0, 0, 0.2); - transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; -} - -input:focus { - color: black; - border-color: #4a90e2; - background-color: #fff; -} - -button { - padding: 12px 20px; - font-size: 16px; - border-radius: 8px; - border: none; - color: #fff; - background-color: rgba(0, 0, 0, 0.4); - cursor: pointer; - transition: background-color 0.3s ease-in-out; -} - -button:hover { - background-color: rgba(0, 0, 0, 1); -} diff --git a/src/components/LoginPage/NameInput/NameInput.jsx b/src/components/LoginPage/NameInput/NameInput.jsx index 964a843..137b026 100644 --- a/src/components/LoginPage/NameInput/NameInput.jsx +++ b/src/components/LoginPage/NameInput/NameInput.jsx @@ -1,8 +1,8 @@ import React, { useState, useContext, useEffect, useCallback } from 'react'; import { SocketContext } from '../../../App'; import useInput from '../../../hooks/useInput'; -import './NameInput.css'; import useKeyPress from '../../../hooks/useKeyPress'; +import styles from './NameInput.module.css'; const NameInput = ({ isRoomPrivate, roomId }) => { const socket = useContext(SocketContext); @@ -10,11 +10,12 @@ const NameInput = ({ isRoomPrivate, roomId }) => { const password = useInput(''); const [isPasswordWrong, setIsPasswordWrong] = useState(false); - const handleButtonClick = useCallback(() => { + const handleButtonClick = () => { socket.emit('player:login', { name: nickname.value, password: password.value, roomId: roomId }); - }, [socket, nickname.value, password.value, roomId]); + }; useKeyPress('Enter', handleButtonClick); + useEffect(() => { socket.on('error:wrongPassword', () => { setIsPasswordWrong(true); @@ -22,13 +23,13 @@ const NameInput = ({ isRoomPrivate, roomId }) => { }, [socket]); return ( -
- +
+ {isRoomPrivate ? ( ) : null} diff --git a/src/components/LoginPage/NameInput/NameInput.module.css b/src/components/LoginPage/NameInput/NameInput.module.css new file mode 100644 index 0000000..a55980e --- /dev/null +++ b/src/components/LoginPage/NameInput/NameInput.module.css @@ -0,0 +1,20 @@ +.container { + display: flex; + flex-direction: column; + padding: 10px 20px 60px 20px; + width: 300px; + background: radial-gradient(circle, rgba(0, 138, 255, 1) 5%, rgba(9, 9, 121, 1) 81%); + border: 1px solid white; + border-radius: 8px; + margin: 20px; + z-index: 2; +} +.container > button { + margin-top: 5px; + text-align: center; + width: 100px; + align-self: center; +} +.container > input { + margin-top: 10px; +} diff --git a/src/components/LoginPage/NameInput/NameInput.test.js b/src/components/LoginPage/NameInput/NameInput.test.js new file mode 100644 index 0000000..44e2702 --- /dev/null +++ b/src/components/LoginPage/NameInput/NameInput.test.js @@ -0,0 +1,89 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import NameInput from './NameInput'; +import { SocketContext } from '../../../App'; + +const mockSocket = { + on: jest.fn(), + emit: jest.fn(), +}; + +describe('NameInput component', () => { + it('should renders password field when room is private', () => { + render( + + + + ); + expect(screen.getByPlaceholderText('Room password')).toBeInTheDocument(); + }); + + it('should not renders password field when room is not private', () => { + render( + + + + ); + expect(screen.queryByPlaceholderText('Room password')).not.toBeInTheDocument(); + }); + + it('should handles input change', () => { + render( + + + + ); + const nicknameInput = screen.getByPlaceholderText('Nickname'); + fireEvent.change(nicknameInput, { target: { value: 'TestName' } }); + expect(nicknameInput.value).toBe('TestName'); + }); + + it('should handles password change', () => { + render( + + + + ); + const passwordInput = screen.getByPlaceholderText('Room password'); + fireEvent.change(passwordInput, { target: { value: 'TestPassword' } }); + expect(passwordInput.value).toBe('TestPassword'); + }); + + it('should handles button click', () => { + render( + + + + ); + const nicknameInput = screen.getByPlaceholderText('Nickname'); + fireEvent.change(nicknameInput, { target: { value: 'TestName' } }); + const passwordInput = screen.getByPlaceholderText('Room password'); + fireEvent.change(passwordInput, { target: { value: 'TestPassword' } }); + const button = screen.getByText('JOIN'); + fireEvent.click(button); + expect(mockSocket.emit).toHaveBeenCalledWith('player:login', { + name: 'TestName', + password: 'TestPassword', + roomId: 123, + }); + }); + + it('should handles Enter key press', () => { + render( + + + + ); + const nicknameInput = screen.getByPlaceholderText('Nickname'); + fireEvent.change(nicknameInput, { target: { value: 'TestName' } }); + const passwordInput = screen.getByPlaceholderText('Room password'); + fireEvent.change(passwordInput, { target: { value: 'TestPassword' } }); + fireEvent.keyDown(nicknameInput, { key: 'Enter' }); + expect(mockSocket.emit).toHaveBeenCalledWith('player:login', { + name: 'TestName', + password: 'TestPassword', + roomId: 123, + }); + }); +}); diff --git a/src/components/LoginPage/ServerList/ServerList.css b/src/components/LoginPage/ServerList/ServerList.css deleted file mode 100644 index 318a008..0000000 --- a/src/components/LoginPage/ServerList/ServerList.css +++ /dev/null @@ -1,51 +0,0 @@ -th { - text-align: left; -} -img { - margin-right: 5px; - width: 20px; - height: 20px; -} -th, -td { - padding: 8px; - text-align: left; - height: 50px; -} -tr { - max-height: 50px; -} - -table { - border-collapse: collapse; - width: 100%; -} -.server-container { - display: flex; - height: 500px; - overflow: scroll; -} -.room-name { - max-width: 150px; - overflow: hidden; -} -/* Firefox */ -* { - scrollbar-width: auto; - scrollbar-color: #ffffff rgba(0, 0, 0, 0.1); -} - -/* Chrome, Edge, and Safari */ -*::-webkit-scrollbar { - background: rgba(0, 0, 0, 0); - width: 10px; -} - -*::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); -} - -*::-webkit-scrollbar-thumb { - background-color: #ffffff; - border-radius: 10px; -} diff --git a/src/components/LoginPage/ServerList/ServerList.jsx b/src/components/LoginPage/ServerList/ServerList.jsx deleted file mode 100644 index c6af53e..0000000 --- a/src/components/LoginPage/ServerList/ServerList.jsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { SocketContext } from '../../../App'; -import lock from '../../../images/login-page/lock.png'; -import refresh from '../../../images/login-page/refresh.png'; -import ReactLoading from 'react-loading'; - -import './ServerList.css'; -import NameInput from '../NameInput/NameInput'; -import Overlay from '../../Overlay/Overlay'; - -const ServerList = () => { - const socket = useContext(SocketContext); - const [rooms, setRooms] = useState([]); - const [joining, setJoining] = useState(false); - const [clickedRoom, setClickedRoom] = useState(null); - useEffect(() => { - socket.emit('room:rooms'); - socket.on('room:rooms', data => { - data = JSON.parse(data); - setRooms(data); - }); - }, [socket]); - - const getRooms = () => { - setRooms(null); - socket.emit('room:rooms'); - }; - - const handleJoinClick = room => { - setClickedRoom(room); - setJoining(true); - }; - - return ( -
-
-

Server List

-
- refresh -
-
-
- {rooms ? ( - - - - - - - - - - - - {rooms.map((room, index) => ( - - - - - - - - ))} - -
Server#/#Status
{room.private ? private : null}{room.name}{`${room.players.length}/4`}{room.isStarted ? 'started' : 'waiting'} - -
- ) : ( -
- -
- )} -
- {joining ? ( - setJoining(false)}> - - - ) : null} -
- ); -}; -export default ServerList; diff --git a/src/components/LoginPage/WindowLayout/WindowLayout.jsx b/src/components/LoginPage/WindowLayout/WindowLayout.jsx new file mode 100644 index 0000000..ee85e85 --- /dev/null +++ b/src/components/LoginPage/WindowLayout/WindowLayout.jsx @@ -0,0 +1,15 @@ +import styles from './WindowLayout.module.css'; + +const WindowLayout = ({ title, titleComponent, content }) => { + return ( +
+
+

{title}

+ {titleComponent} +
+
{content}
+
+ ); +}; + +export default WindowLayout; diff --git a/src/components/LoginPage/LoginPage.css b/src/components/LoginPage/WindowLayout/WindowLayout.module.css similarity index 76% rename from src/components/LoginPage/LoginPage.css rename to src/components/LoginPage/WindowLayout/WindowLayout.module.css index 3376c6f..ca9f74e 100644 --- a/src/components/LoginPage/LoginPage.css +++ b/src/components/LoginPage/WindowLayout/WindowLayout.module.css @@ -1,25 +1,14 @@ -.login-page-container { - display: flex; - flex-direction: row; - - justify-content: center; - align-items: flex-start; - height: 50%; - width: 100%; -} - -.lp-container { +.container { margin: 50px; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 500px; - padding: 20px; color: white; } -.title-container { +.title { display: flex; flex-direction: row; justify-content: center; @@ -34,17 +23,21 @@ text-align: center; } -.title-container > h1 { +.title > h1 { width: 100%; margin: 0; padding: 0; } -.content-container { +.content { display: flex; flex-direction: column; + justify-content: center; + align-items: center; width: 100%; - padding: 10px; + padding-left: 5px; + padding-right: 5px; + padding-top: 10px; background-color: rgba(0, 0, 0, 0.5); box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); border-left: 1px solid black; diff --git a/src/components/LoginPage/WindowLayout/WindowLayout.test.js b/src/components/LoginPage/WindowLayout/WindowLayout.test.js new file mode 100644 index 0000000..2bd1827 --- /dev/null +++ b/src/components/LoginPage/WindowLayout/WindowLayout.test.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import WindowLayout from './WindowLayout'; + +jest.mock('./WindowLayout', () => ({ title, titleComponent, content }) => ( +
+
{title}
+
{titleComponent}
+
{content}
+
+)); + +describe('WindowLayout component', () => { + it('should render without crashing', () => { + render( + Test Title Component
} + content={
Test Content
} + /> + ); + expect(screen.getByTestId('mocked-window-layout')).toBeInTheDocument(); + expect(screen.getByTestId('mocked-title')).toHaveTextContent('Test Title'); + expect(screen.getByTestId('mocked-title-component')).toHaveTextContent('Test Title Component'); + expect(screen.getByTestId('mocked-content')).toHaveTextContent('Test Content'); + }); +}); diff --git a/src/hooks/useInput.js b/src/hooks/useInput.js index 1144f96..a933dc3 100644 --- a/src/hooks/useInput.js +++ b/src/hooks/useInput.js @@ -1,11 +1,11 @@ import { useState } from 'react'; -export default function useInput({ initialValue }) { +export default function useInput(initialValue = '') { const [value, setValue] = useState(initialValue); const handleChange = e => { setValue(e.target.value); }; return { - value, + value: value, onChange: handleChange, }; } diff --git a/src/hooks/useKeyPress.js b/src/hooks/useKeyPress.js index 072a74c..70e4a19 100644 --- a/src/hooks/useKeyPress.js +++ b/src/hooks/useKeyPress.js @@ -11,5 +11,5 @@ export default function useKeyPress(targetKey, callback) { return () => { window.removeEventListener('keydown', keyPressHandler); }; - }, []); + }, [keyPressHandler]); } diff --git a/src/hooks/useSocketData.js b/src/hooks/useSocketData.js index d33b7dc..527bc84 100644 --- a/src/hooks/useSocketData.js +++ b/src/hooks/useSocketData.js @@ -4,8 +4,14 @@ import { SocketContext } from '../App'; const useSocketData = port => { const socket = useContext(SocketContext); const [data, setData] = useState(null); - socket.on(port, data => { - setData(data); + socket.on(port, res => { + let parsedData; + try { + parsedData = JSON.parse(res); + } catch (error) { + parsedData = res; + } + setData(parsedData); }); return [data, setData]; }; diff --git a/src/index.css b/src/index.css index cae3a75..a7f45a9 100644 --- a/src/index.css +++ b/src/index.css @@ -67,3 +67,70 @@ canvas { grid-column: 1 / span 2; grid-row: 2 / span 2; } + +input, +button { + padding: 0; + border: none; + outline: none; + box-sizing: border-box; +} + +input { + width: 100%; + padding: 12px; + font-size: 16px; + border-radius: 8px; + color: white; + border: 1px solid #ccc; + background-color: rgba(0, 0, 0, 0.2); + transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; +} + +input:disabled { + background-color: black; + color: #999; + border: 1px solid #ddd; +} + +input:focus { + color: black; + border-color: #4a90e2; + background-color: #fff; +} + +button { + padding: 12px 20px; + font-size: 16px; + border-radius: 8px; + border: none; + color: #fff; + background-color: rgba(0, 0, 0, 0.4); + cursor: pointer; + transition: background-color 0.3s ease-in-out; +} + +button:hover { + background-color: rgba(0, 0, 0, 1); +} + +/* Firefox */ +* { + scrollbar-width: auto; + scrollbar-color: #ffffff rgba(0, 0, 0, 0.1); +} + +/* Chrome, Edge, and Safari */ +*::-webkit-scrollbar { + background: rgba(0, 0, 0, 0); + width: 10px; +} + +*::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0); +} + +*::-webkit-scrollbar-thumb { + background-color: #ffffff; + border-radius: 10px; +}