From 1576f23a1d52a88092eb88f1e26c8c593a9d1acb Mon Sep 17 00:00:00 2001 From: Natalia Date: Thu, 16 Jun 2022 22:50:03 -0400 Subject: [PATCH 1/6] completed wave 1 --- src/App.js | 13 +++++++++++++ src/components/ChatEntry.js | 14 ++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/App.js b/src/App.js index c10859093..75505da06 100644 --- a/src/App.js +++ b/src/App.js @@ -1,14 +1,27 @@ import React from 'react'; import './App.css'; +import ChatEntry from './components/ChatEntry'; import chatMessages from './data/messages.json'; const App = () => { + const chatData = { + id: 1, + sender: 'Vladimir', + body: 'why are you arguing with me', + timeStamp: '2018-05-29T22:49:06+00:00', + liked: false, + }; return (

Application title

+ {/* Wave 01: Render one ChatEntry component Wave 02: Render ChatLog component */}
diff --git a/src/components/ChatEntry.js b/src/components/ChatEntry.js index b92f0b7b2..322361e59 100644 --- a/src/components/ChatEntry.js +++ b/src/components/ChatEntry.js @@ -1,14 +1,16 @@ import React from 'react'; import './ChatEntry.css'; import PropTypes from 'prop-types'; +import moment from 'moment'; const ChatEntry = (props) => { + const timeAgo = moment(props.timeStamp).fromNow(); return (
-

Replace with name of sender

+

{props.sender}

-

Replace with body of ChatEntry

-

Replace with TimeStamp component

+

{props.body}

+

{timeAgo}

@@ -16,7 +18,11 @@ const ChatEntry = (props) => { }; ChatEntry.propTypes = { - //Fill with correct proptypes + id: PropTypes.number, + sender: PropTypes.string, + body: PropTypes.string, + timestamp: PropTypes.string, + liked: PropTypes.bool, }; export default ChatEntry; From 366b0f1df245a500c9be6b024811bba2758d3508 Mon Sep 17 00:00:00 2001 From: Natalia Date: Fri, 17 Jun 2022 11:52:47 -0400 Subject: [PATCH 2/6] completed wave 2, add entire chatlog --- package.json | 5 +++-- src/App.js | 9 ++------- src/components/ChatEntry.js | 4 +++- src/components/ChatEntry.test.js | 16 ++++++++-------- src/components/ChatLog.js | 17 +++++++++++++++++ yarn.lock | 5 +++++ 6 files changed, 38 insertions(+), 18 deletions(-) create mode 100644 src/components/ChatLog.js diff --git a/package.json b/package.json index 94ac60247..6749e64d1 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", + "luxon": "^2.4.0", + "moment": "^2.29.3", "react": "^17.0.1", "react-dom": "^17.0.1", - "react-scripts": "4.0.1", - "luxon": "^2.4.0" + "react-scripts": "4.0.1" }, "scripts": { "start": "react-scripts start", diff --git a/src/App.js b/src/App.js index 75505da06..74d93cff2 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,7 @@ import React from 'react'; import './App.css'; import ChatEntry from './components/ChatEntry'; +import ChatLog from './components/ChatLog'; import chatMessages from './data/messages.json'; const App = () => { @@ -17,13 +18,7 @@ const App = () => {

Application title

- - {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.js b/src/components/ChatEntry.js index 322361e59..a8c853562 100644 --- a/src/components/ChatEntry.js +++ b/src/components/ChatEntry.js @@ -5,8 +5,10 @@ import moment from 'moment'; const ChatEntry = (props) => { const timeAgo = moment(props.timeStamp).fromNow(); + let entrySide = + props.sender === 'Vladimir' ? 'chat-entry local' : 'chat-entry remote'; return ( -
+

{props.sender}

{props.body}

diff --git a/src/components/ChatEntry.test.js b/src/components/ChatEntry.test.js index b69270c03..4a9a2b09f 100644 --- a/src/components/ChatEntry.test.js +++ b/src/components/ChatEntry.test.js @@ -1,9 +1,9 @@ -import React from "react"; -import "@testing-library/jest-dom/extend-expect"; -import ChatEntry from "./ChatEntry"; -import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import React from 'react'; +import '@testing-library/jest-dom/extend-expect'; +import ChatEntry from './ChatEntry'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; -describe("Wave 01: ChatEntry", () => { +describe('Wave 01: ChatEntry', () => { beforeEach(() => { render( { ); }); - test("renders without crashing and shows the sender", () => { + test('renders without crashing and shows the sender', () => { expect(screen.getByText(/Joe Biden/)).toBeInTheDocument(); }); - test("that it will display the body", () => { + test('that it will display the body', () => { expect(screen.getByText(/Get out by 8am/)).toBeInTheDocument(); }); - test("that it will display the time", () => { + test('that it will display the time', () => { expect(screen.getByText(/\d+ years ago/)).toBeInTheDocument(); }); }); diff --git a/src/components/ChatLog.js b/src/components/ChatLog.js new file mode 100644 index 000000000..bb0ef3daf --- /dev/null +++ b/src/components/ChatLog.js @@ -0,0 +1,17 @@ +import react from 'react'; +import ChatEntry from './ChatEntry'; + +const ChatLog = (props) => { + const chatEntries = props.entries.map((entry) => { + return ( + + ); + }); + return
{chatEntries}
; +}; + +export default ChatLog; diff --git a/yarn.lock b/yarn.lock index d4bf966c0..c9bc977b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7374,6 +7374,11 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +moment@^2.29.3: + version "2.29.3" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3" + integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" From 18afe2385976e7d20f8fe23c5d363c9a334d733f Mon Sep 17 00:00:00 2001 From: Natalia Date: Mon, 20 Jun 2022 18:32:12 -0400 Subject: [PATCH 3/6] completed wave 3, added update like status and count --- src/App.js | 35 +++++++++++++++------- src/App.test.js | 60 ++++++++++++++++++------------------- src/components/ChatEntry.js | 19 ++++++++++-- src/components/ChatLog.js | 11 ++++++- 4 files changed, 82 insertions(+), 43 deletions(-) diff --git a/src/App.js b/src/App.js index 74d93cff2..b3ce6bdf6 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,39 @@ -import React from 'react'; +import React, { useState } from 'react'; import './App.css'; -import ChatEntry from './components/ChatEntry'; import ChatLog from './components/ChatLog'; import chatMessages from './data/messages.json'; const App = () => { - const chatData = { - id: 1, - sender: 'Vladimir', - body: 'why are you arguing with me', - timeStamp: '2018-05-29T22:49:06+00:00', - liked: false, + const [chatData, setChatData] = useState(chatMessages); + + const updateChatData = (updatedEntry) => { + const entries = chatData.map((entry) => { + if (entry.id === updatedEntry.id) { + return updatedEntry; + } else { + return entry; + } + }); + setChatData(entries); }; + + const totalLikes = chatData.reduce((total, entry) => { + if (entry.liked === true) { + total += 1; + } + return total; + }, 0); + return (
-

Application title

+

+
Chat Log
+
{totalLikes} ❤️s
+

- +
); diff --git a/src/App.test.js b/src/App.test.js index ca75c71dd..878148902 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,53 +1,53 @@ -import React from 'react' -import App from './App' -import { render, screen, fireEvent } from '@testing-library/react' +import React from 'react'; +import App from './App'; +import { render, screen, fireEvent } from '@testing-library/react'; describe('Wave 03: clicking like button and rendering App', () => { test('that the correct number of likes is printed at the top', () => { // Arrange - const { container } = render() - let buttons = container.querySelectorAll('button.like') + const { container } = render(); + let buttons = container.querySelectorAll('button.like'); // Act - fireEvent.click(buttons[0]) - fireEvent.click(buttons[1]) - fireEvent.click(buttons[10]) + fireEvent.click(buttons[0]); + fireEvent.click(buttons[1]); + fireEvent.click(buttons[10]); // Assert - const countScreen = screen.getByText(/3 ❤️s/) - expect(countScreen).not.toBeNull() - }) + const countScreen = screen.getByText(/3 ❤️s/); + expect(countScreen).not.toBeNull(); + }); test('clicking button toggles heart and does not affect other buttons', () => { // Arrange - const { container } = render() - const buttons = container.querySelectorAll('button.like') - const firstButton = buttons[0] - const lastButton = buttons[buttons.length - 1] + const { container } = render(); + const buttons = container.querySelectorAll('button.like'); + const firstButton = buttons[0]; + const lastButton = buttons[buttons.length - 1]; // Act-Assert // click the first button - fireEvent.click(firstButton) - expect(firstButton.innerHTML).toEqual('❤️') + fireEvent.click(firstButton); + expect(firstButton.innerHTML).toEqual('❤️'); // check that all other buttons haven't changed for (let i = 1; i < buttons.length; i++) { - expect(buttons[i].innerHTML).toEqual('🤍') + expect(buttons[i].innerHTML).toEqual('🤍'); } // click the first button a few more times - fireEvent.click(firstButton) - expect(firstButton.innerHTML).toEqual('🤍') - fireEvent.click(firstButton) - expect(firstButton.innerHTML).toEqual('❤️') - fireEvent.click(firstButton) - expect(firstButton.innerHTML).toEqual('🤍') + fireEvent.click(firstButton); + expect(firstButton.innerHTML).toEqual('🤍'); + fireEvent.click(firstButton); + expect(firstButton.innerHTML).toEqual('❤️'); + fireEvent.click(firstButton); + expect(firstButton.innerHTML).toEqual('🤍'); // click the last button a couple times - fireEvent.click(lastButton) - expect(lastButton.innerHTML).toEqual('❤️') - fireEvent.click(lastButton) - expect(lastButton.innerHTML).toEqual('🤍') - }) -}) + fireEvent.click(lastButton); + expect(lastButton.innerHTML).toEqual('❤️'); + fireEvent.click(lastButton); + expect(lastButton.innerHTML).toEqual('🤍'); + }); +}); diff --git a/src/components/ChatEntry.js b/src/components/ChatEntry.js index a8c853562..e93d77cac 100644 --- a/src/components/ChatEntry.js +++ b/src/components/ChatEntry.js @@ -5,26 +5,41 @@ import moment from 'moment'; const ChatEntry = (props) => { const timeAgo = moment(props.timeStamp).fromNow(); + const heartStatus = props.liked ? '❤️' : '🤍'; let entrySide = props.sender === 'Vladimir' ? 'chat-entry local' : 'chat-entry remote'; + + const handleLike = () => { + const updatedEntry = { + id: props.id, + sender: props.sender, + body: props.body, + liked: !props.liked, + }; + props.onUpdate(updatedEntry); + }; + return (

{props.sender}

{props.body}

{timeAgo}

- +
); }; ChatEntry.propTypes = { - id: PropTypes.number, + id: PropTypes.number.isRequired, sender: PropTypes.string, body: PropTypes.string, timestamp: PropTypes.string, liked: PropTypes.bool, + onUpdate: PropTypes.func.isRequired, }; export default ChatEntry; diff --git a/src/components/ChatLog.js b/src/components/ChatLog.js index bb0ef3daf..898222ce4 100644 --- a/src/components/ChatLog.js +++ b/src/components/ChatLog.js @@ -1,17 +1,26 @@ -import react from 'react'; import ChatEntry from './ChatEntry'; +import PropTypes from 'prop-types'; const ChatLog = (props) => { const chatEntries = props.entries.map((entry) => { return ( ); }); return
{chatEntries}
; }; +ChatLog.propTypes = { + entries: PropTypes.array.isRequired, + onUpdateChat: PropTypes.func.isRequired, +}; + export default ChatLog; From 18440a3c9c0c8c524abd7aa5e1bf19e364240971 Mon Sep 17 00:00:00 2001 From: Natalia Date: Wed, 22 Jun 2022 00:04:41 -0400 Subject: [PATCH 4/6] completed optional enhancement, color choice --- src/App.css | 11 +++++++++-- src/App.js | 21 ++++++++++++++++++++- src/components/ChatEntry.js | 13 +++++++++---- src/components/ChatLog.js | 2 ++ src/components/ColorChoice.js | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 src/components/ColorChoice.js diff --git a/src/App.css b/src/App.css index d97beb4e6..697e7a881 100644 --- a/src/App.css +++ b/src/App.css @@ -4,7 +4,7 @@ #App header { background-color: #222; - color: #fff; + /* color: #fff; */ padding-bottom: 0.5rem; position: fixed; width: 100%; @@ -24,6 +24,7 @@ font-size: 1.5em; text-align: center; display: inline-block; + color: #fff; } #App header section { @@ -71,4 +72,10 @@ .purple { color: purple -} \ No newline at end of file +} + +.colorChoice { + display: flex; + justify-content: space-around; +} + diff --git a/src/App.js b/src/App.js index b3ce6bdf6..e285cc903 100644 --- a/src/App.js +++ b/src/App.js @@ -2,9 +2,12 @@ import React, { useState } from 'react'; import './App.css'; import ChatLog from './components/ChatLog'; import chatMessages from './data/messages.json'; +import ColorChoice from './components/ColorChoice'; const App = () => { const [chatData, setChatData] = useState(chatMessages); + const [localTextColor, setLocalTextColor] = useState('black'); + const [remoteTextColor, setRemoteTextColor] = useState('black'); const updateChatData = (updatedEntry) => { const entries = chatData.map((entry) => { @@ -31,9 +34,25 @@ const App = () => {
Chat Log
{totalLikes} ❤️s
+
+
+

Vladimir's color

+ +
+
+

Estragon's color

+ +
+
+
- +
); diff --git a/src/components/ChatEntry.js b/src/components/ChatEntry.js index e93d77cac..2a3b6e051 100644 --- a/src/components/ChatEntry.js +++ b/src/components/ChatEntry.js @@ -1,20 +1,23 @@ import React from 'react'; import './ChatEntry.css'; import PropTypes from 'prop-types'; -import moment from 'moment'; +import TimeStamp from './TimeStamp'; const ChatEntry = (props) => { - const timeAgo = moment(props.timeStamp).fromNow(); const heartStatus = props.liked ? '❤️' : '🤍'; let entrySide = props.sender === 'Vladimir' ? 'chat-entry local' : 'chat-entry remote'; + let color = + props.sender === 'Vladimir' ? props.localTextColor : props.remoteTextColor; + const handleLike = () => { const updatedEntry = { id: props.id, sender: props.sender, body: props.body, liked: !props.liked, + timeStamp: props.timeStamp, }; props.onUpdate(updatedEntry); }; @@ -23,8 +26,10 @@ const ChatEntry = (props) => {

{props.sender}

-

{props.body}

-

{timeAgo}

+

{props.body}

+

+ +

diff --git a/src/components/ChatLog.js b/src/components/ChatLog.js index 898222ce4..99c89ff60 100644 --- a/src/components/ChatLog.js +++ b/src/components/ChatLog.js @@ -12,6 +12,8 @@ const ChatLog = (props) => { timeStamp={entry.timeStamp} liked={entry.liked} onUpdate={props.onUpdateChat} + localTextColor={props.onUpdateLocal} + remoteTextColor={props.onUpdateRemote} > ); }); diff --git a/src/components/ColorChoice.js b/src/components/ColorChoice.js new file mode 100644 index 000000000..40bc91447 --- /dev/null +++ b/src/components/ColorChoice.js @@ -0,0 +1,35 @@ +import React from 'react'; + +const ColorChoice = (props) => { + const setColorCallback = (event) => { + if (props.onUpdateLocal) { + props.onUpdateLocal(event.currentTarget.id); + } else { + props.onUpdateRemote(event.currentTarget.id); + } + }; + return ( +
+ + + + + + +
+ ); +}; + +export default ColorChoice; From 232147391ec296acc8fc8450713eededb3cb4547 Mon Sep 17 00:00:00 2001 From: Natalia Date: Wed, 22 Jun 2022 11:55:53 -0400 Subject: [PATCH 5/6] refactored colorChoice component for creating buttons --- src/components/ColorChoice.js | 41 ++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/components/ColorChoice.js b/src/components/ColorChoice.js index 40bc91447..61410b4f3 100644 --- a/src/components/ColorChoice.js +++ b/src/components/ColorChoice.js @@ -8,28 +8,25 @@ const ColorChoice = (props) => { props.onUpdateRemote(event.currentTarget.id); } }; - return ( -
- - - - - - -
- ); + + const getButtonsMap = () => { + const colors = { + red: '🔴', + yellow: '🟡', + purple: '🟣', + green: '🟢', + orange: '🟠', + blue: '🔵', + }; + return Object.entries(colors).map(([key, value]) => { + return ( + + ); + }); + }; + return
{getButtonsMap()}
; }; export default ColorChoice; From daef77351bdd7b5c1f4e7118de7eb12d9c71f3c8 Mon Sep 17 00:00:00 2001 From: Natalia Date: Wed, 22 Jun 2022 18:01:18 -0400 Subject: [PATCH 6/6] added proptypes to ColorChoice component --- src/components/ColorChoice.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/ColorChoice.js b/src/components/ColorChoice.js index 61410b4f3..a5ee47139 100644 --- a/src/components/ColorChoice.js +++ b/src/components/ColorChoice.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; const ColorChoice = (props) => { const setColorCallback = (event) => { @@ -29,4 +30,9 @@ const ColorChoice = (props) => { return
{getButtonsMap()}
; }; +ColorChoice.propTypes = { + onUpdateLocal: PropTypes.func, + onUpdateRemote: PropTypes.func, +}; + export default ColorChoice;