From 2b866db20417b5c4e336bf4c6104aa87943db3ce Mon Sep 17 00:00:00 2001 From: KateM Date: Tue, 10 Jun 2025 15:58:10 -0700 Subject: [PATCH 1/6] Wave_1 done, tests passed --- package-lock.json | 11 +++++ package.json | 1 + src/components/ChatEntry.jsx | 85 +++++++++++++++++++++++++++++++++--- 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a6ed1d5b..6992a2558 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "react-chatlog", "version": "0.0.0", "dependencies": { + "date-fns": "^4.1.0", "luxon": "^2.5.2", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -2264,6 +2265,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", diff --git a/package.json b/package.json index 1a053dda4..404f3da32 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test": "vitest --run" }, "dependencies": { + "date-fns": "^4.1.0", "luxon": "^2.5.2", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 15c56f96b..597081e3b 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,20 +1,91 @@ import './ChatEntry.css'; +import PropTypes from 'prop-types'; +import TimeStamp from './TimeStamp'; +// import messages from './data/messages.json'; -const ChatEntry = () => { + +const ChatEntry = (props) => { return ( -
-

Replace with name of sender

+
+

{props.sender}

-

Replace with body of ChatEntry

-

Replace with TimeStamp component

- +

{props.body}

+

+
); }; ChatEntry.propTypes = { - // Fill with correct proptypes + id: PropTypes.string.isRequired, + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired }; export default ChatEntry; + +// const index = 0; // This should be replaced with the actual index of the message you want to display + +// const ChatEntry = (props) => { +// const chatComponents = props.chatData.map((message, index) => { +// return ( +//
+//

{message.name}

+//
+//

{message.body}

+//

{formatDistanceToNow(new Date(message.timeStamp), { addSuffix: true })}

+// +//
+//
+// ); +// }); +// return <>{chatComponents}; +// }; + +// const SenderBody = messages[index].body; +// const SenderTimeStamp = messages[index].timeStamp; +// const SenderLiked = messages[index].liked; +// // possibly add later about timeAgo +// // const timeAgo = formatDistanceToNow(new Date(timeStamp), { +// // addSuffix: true, +// // }); +// return ( +//
+//

{SenderName}

+//
+//

{SenderBody}

+//

{formatDistanceToNow(new Date(SenderTimeStamp), { addSuffix: true })}

+// +//
+//
+// ); +// }; + + + + +// const ChatEntry = ({ }) => { +// const SenderName = messages[index].name; +// const SenderBody = messages[index].body; +// const SenderTimeStamp = messages[index].timeStamp; +// const SenderLiked = messages[index].liked; +// // possibly add later about timeAgo +// // const timeAgo = formatDistanceToNow(new Date(timeStamp), { +// // addSuffix: true, +// // }); +// return ( +//
+//

{SenderName}

+//
+//

{SenderBody}

+//

{formatDistanceToNow(new Date(SenderTimeStamp), { addSuffix: true })}

+// +//
+//
+// ); +// }; + + From 9b6b9c7bb09266864013ca781115a50f99327720 Mon Sep 17 00:00:00 2001 From: KateM Date: Tue, 10 Jun 2025 16:35:54 -0700 Subject: [PATCH 2/6] 1st bubble rendered --- src/App.jsx | 5 +++-- src/components/ChatEntry.jsx | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 14a7f684d..9050c1783 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,6 @@ import './App.css'; +import messages from './data/messages.json'; +import ChatEntry from './components/ChatEntry'; const App = () => { return ( @@ -7,8 +9,7 @@ const App = () => {

Application title

- {/* Wave 01: Render one ChatEntry component - Wave 02: Render ChatLog component */} +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 597081e3b..c2f356645 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -1,7 +1,6 @@ import './ChatEntry.css'; import PropTypes from 'prop-types'; import TimeStamp from './TimeStamp'; -// import messages from './data/messages.json'; const ChatEntry = (props) => { @@ -18,7 +17,7 @@ const ChatEntry = (props) => { }; ChatEntry.propTypes = { - id: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, From 4f7a8bda33fb3a4a1e5bb287d7b22e969e667e38 Mon Sep 17 00:00:00 2001 From: KateM Date: Wed, 11 Jun 2025 14:11:22 -0700 Subject: [PATCH 3/6] Wave 2 done, tests passed --- eslint.config.mjs | 2 +- src/App.jsx | 24 +++---- src/components/ChatEntry.jsx | 95 +++++-------------------- src/components/ChatLog.jsx | 25 +++++++ src/components/ChatLog.test.jsx | 120 ++++++++++++++++---------------- 5 files changed, 116 insertions(+), 150 deletions(-) create mode 100644 src/components/ChatLog.jsx diff --git a/eslint.config.mjs b/eslint.config.mjs index 6c2b6b501..bde2a6b3d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -65,7 +65,7 @@ export default [{ "comma-dangle": ["warn", "only-multiline"], "dot-notation": "warn", "space-before-function-paren": "off", - "indent": ["warn", 2], + "indent": ["warn", 'tab'], "padded-blocks": "warn", "no-trailing-spaces": "warn", "array-bracket-spacing": "warn", diff --git a/src/App.jsx b/src/App.jsx index 9050c1783..f17c8c71f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,18 +1,18 @@ import './App.css'; -import messages from './data/messages.json'; -import ChatEntry from './components/ChatEntry'; +import entries from './data/messages.json'; +import ChatLog from './components/ChatLog'; const App = () => { - return ( -
-
-

Application title

-
-
- -
-
- ); + return ( +
+
+

Application title

+
+
+ +
+
+ ); }; export default App; diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index c2f356645..a61dbe0d3 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -4,87 +4,28 @@ import TimeStamp from './TimeStamp'; const ChatEntry = (props) => { - return ( -
-

{props.sender}

-
-

{props.body}

-

- -
-
- ); + return ( +
+

{props.sender}

+
+

{props.body}

+

+ +
+
+ ); }; ChatEntry.propTypes = { - id: PropTypes.number.isRequired, - sender: PropTypes.string.isRequired, - body: PropTypes.string.isRequired, - timeStamp: PropTypes.string.isRequired, - liked: PropTypes.bool.isRequired + id: PropTypes.number.isRequired, + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired }; export default ChatEntry; -// const index = 0; // This should be replaced with the actual index of the message you want to display - -// const ChatEntry = (props) => { -// const chatComponents = props.chatData.map((message, index) => { -// return ( -//
-//

{message.name}

-//
-//

{message.body}

-//

{formatDistanceToNow(new Date(message.timeStamp), { addSuffix: true })}

-// -//
-//
-// ); -// }); -// return <>{chatComponents}; -// }; - -// const SenderBody = messages[index].body; -// const SenderTimeStamp = messages[index].timeStamp; -// const SenderLiked = messages[index].liked; -// // possibly add later about timeAgo -// // const timeAgo = formatDistanceToNow(new Date(timeStamp), { -// // addSuffix: true, -// // }); -// return ( -//
-//

{SenderName}

-//
-//

{SenderBody}

-//

{formatDistanceToNow(new Date(SenderTimeStamp), { addSuffix: true })}

-// -//
-//
-// ); -// }; - - - - -// const ChatEntry = ({ }) => { -// const SenderName = messages[index].name; -// const SenderBody = messages[index].body; -// const SenderTimeStamp = messages[index].timeStamp; -// const SenderLiked = messages[index].liked; -// // possibly add later about timeAgo -// // const timeAgo = formatDistanceToNow(new Date(timeStamp), { -// // addSuffix: true, -// // }); -// return ( -//
-//

{SenderName}

-//
-//

{SenderBody}

-//

{formatDistanceToNow(new Date(SenderTimeStamp), { addSuffix: true })}

-// -//
-//
-// ); -// }; - - +{/*
+ +
; */} \ No newline at end of file diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx new file mode 100644 index 000000000..83e81169f --- /dev/null +++ b/src/components/ChatLog.jsx @@ -0,0 +1,25 @@ +// import entries from './data/messages.json'; +import PropTypes from 'prop-types'; +import ChatEntry from './ChatEntry'; + +const ChatLog = ({entries}) => { + return ( +
+ {entries.map((entry) => ( + + ))} +
+); +}; +ChatLog.propTypes = { + entries: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + sender: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, + timeStamp: PropTypes.string.isRequired, + liked: PropTypes.bool.isRequired + }) + ).isRequired +}; +export default ChatLog; diff --git a/src/components/ChatLog.test.jsx b/src/components/ChatLog.test.jsx index dfcfeda99..9b8d5815f 100644 --- a/src/components/ChatLog.test.jsx +++ b/src/components/ChatLog.test.jsx @@ -3,70 +3,70 @@ import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; const LOG = [ - { - id: 1, - sender: 'Vladimir', - body: 'why are you arguing with me', - timeStamp: '2018-05-29T22:49:06+00:00', - liked: false, - }, - { - id: 2, - sender: 'Estragon', - body: 'Because you are wrong.', - timeStamp: '2018-05-29T22:49:33+00:00', - liked: false, - }, - { - id: 3, - sender: 'Vladimir', - body: 'because I am what', - timeStamp: '2018-05-29T22:50:22+00:00', - liked: false, - }, - { - id: 4, - sender: 'Estragon', - body: 'A robot.', - timeStamp: '2018-05-29T22:52:21+00:00', - liked: false, - }, - { - id: 5, - sender: 'Vladimir', - body: 'Notabot', - timeStamp: '2019-07-23T22:52:21+00:00', - liked: false, - }, + { + id: 1, + sender: 'Vladimir', + body: 'why are you arguing with me', + timeStamp: '2018-05-29T22:49:06+00:00', + liked: false, + }, + { + id: 2, + sender: 'Estragon', + body: 'Because you are wrong.', + timeStamp: '2018-05-29T22:49:33+00:00', + liked: false, + }, + { + id: 3, + sender: 'Vladimir', + body: 'because I am what', + timeStamp: '2018-05-29T22:50:22+00:00', + liked: false, + }, + { + id: 4, + sender: 'Estragon', + body: 'A robot.', + timeStamp: '2018-05-29T22:52:21+00:00', + liked: false, + }, + { + id: 5, + sender: 'Vladimir', + body: 'Notabot', + timeStamp: '2019-07-23T22:52:21+00:00', + liked: false, + }, ]; describe('Wave 02: ChatLog', () => { - beforeEach(() => { - render(); - }); + beforeEach(() => { + render(); + }); - test('renders without crashing and shows all the names', () => { - [ - { - name: 'Vladimir', - numChats: 3, - }, - { - name: 'Estragon', - numChats: 2, - }, - ].forEach((person) => { - const elementList = screen.getAllByText(new RegExp(person.name)); - expect(elementList.length).toEqual(person.numChats); + test('renders without crashing and shows all the names', () => { + [ + { + name: 'Vladimir', + numChats: 3, + }, + { + name: 'Estragon', + numChats: 2, + }, + ].forEach((person) => { + const elementList = screen.getAllByText(new RegExp(person.name)); + expect(elementList.length).toEqual(person.numChats); - elementList.forEach((element) => { - expect(element).toBeInTheDocument(); - }); - }); - }); + elementList.forEach((element) => { + expect(element).toBeInTheDocument(); + }); + }); + }); - test('renders an empty list without crashing', () => { - const element = render(); - expect(element).not.toBeNull(); - }); + test('renders an empty list without crashing', () => { + const element = render(); + expect(element).not.toBeNull(); + }); }); From 289a2b314d267fb75aca4ffcd4d2b20ab7db21a3 Mon Sep 17 00:00:00 2001 From: KateM Date: Thu, 12 Jun 2025 18:37:32 -0700 Subject: [PATCH 4/6] Wave 3 done, made some changes to CSS --- src/App.css | 22 ++++++++++++++-------- src/App.jsx | 21 ++++++++++++++++++--- src/components/ChatEntry.jsx | 19 ++++++++++++------- src/components/ChatLog.jsx | 8 +++++--- 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/App.css b/src/App.css index d97beb4e6..92db5b79b 100644 --- a/src/App.css +++ b/src/App.css @@ -31,18 +31,24 @@ } #App .widget { - display: inline-block; - line-height: 0.5em; - border-radius: 10px; + position: fixed; + top: 9.6vh; + width: 100%; + height: 9vh; + background-color: #e0ffff; color: black; - font-size:0.8em; - padding-left: 1em; - padding-right: 1em; -} + font-size: 1.5em; + text-align: center; + display: flex; /* enables centering */ + align-items: center; /* vertical centering */ + justify-content: center; /* horizontal centering */ + box-sizing: border-box; + z-index: 90; + #App #heartWidget { font-size: 1.5em; - margin: 1em + margin: 0em } #App span { diff --git a/src/App.jsx b/src/App.jsx index f17c8c71f..0c5366443 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,15 +1,30 @@ import './App.css'; -import entries from './data/messages.json'; +import entriesData from './data/messages.json'; import ChatLog from './components/ChatLog'; +import { useState } from 'react'; const App = () => { + const [entries, setEntries] = useState(entriesData); + const toggleLike = (id) => { + const updatedEntries = entries.map(entry => { + if (entry.id === id) { + return { ...entry, liked: !entry.liked } ; + } else{ + return entry; + } + }); + setEntries(updatedEntries); + }; + const totalLikes = entries.filter((entry) => entry.liked).length; + return (
-

Application title

+

Chat between {entries[0].sender} and {entries[1].sender}

+
{totalLikes}❤️{totalLikes !== 1 ? 's' : ''}
- +
); diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index a61dbe0d3..52310c789 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -4,13 +4,20 @@ import TimeStamp from './TimeStamp'; const ChatEntry = (props) => { + const localSender = props.sender === 'Vladimir'; + const entryClass = `chat-entry ${localSender ? 'local' : 'remote'}`; + // const likedStatus = () => { + // props.liked ? 'liked' : 'not-liked'; + return ( -
+

{props.sender}

{props.body}

- +
); @@ -21,11 +28,9 @@ ChatEntry.propTypes = { sender: PropTypes.string.isRequired, body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, - liked: PropTypes.bool.isRequired + liked: PropTypes.bool.isRequired, + onToggleLike: PropTypes.func.isRequired }; -export default ChatEntry; -{/*
- -
; */} \ No newline at end of file +export default ChatEntry; \ No newline at end of file diff --git a/src/components/ChatLog.jsx b/src/components/ChatLog.jsx index 83e81169f..b33bc379b 100644 --- a/src/components/ChatLog.jsx +++ b/src/components/ChatLog.jsx @@ -2,11 +2,11 @@ import PropTypes from 'prop-types'; import ChatEntry from './ChatEntry'; -const ChatLog = ({entries}) => { +const ChatLog = ({ entries, onToggleLike }) => { return (
{entries.map((entry) => ( - + ))}
); @@ -20,6 +20,8 @@ ChatLog.propTypes = { timeStamp: PropTypes.string.isRequired, liked: PropTypes.bool.isRequired }) - ).isRequired + ).isRequired, + onToggleLike: PropTypes.func.isRequired, }; + export default ChatLog; From 73ba72c785f9c1abae97de9631046dc9985d0ea9 Mon Sep 17 00:00:00 2001 From: KateM Date: Thu, 12 Jun 2025 22:25:37 -0700 Subject: [PATCH 5/6] Made small correction to pass the test for Wave 3 --- src/App.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.jsx b/src/App.jsx index 0c5366443..055758efd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -22,7 +22,7 @@ const App = () => {

Chat between {entries[0].sender} and {entries[1].sender}

-
{totalLikes}❤️{totalLikes !== 1 ? 's' : ''}
+
{totalLikes} ❤️{totalLikes !== 1 ? 's' : ''}
From 0752ba473e023af85f30d07b6dfb51a1b8d76f78 Mon Sep 17 00:00:00 2001 From: KateM Date: Mon, 16 Jun 2025 12:41:26 -0700 Subject: [PATCH 6/6] modified ChatEntry to pass the tests from Waves 1 and 2 --- src/components/ChatEntry.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ChatEntry.jsx b/src/components/ChatEntry.jsx index 52310c789..137b181df 100644 --- a/src/components/ChatEntry.jsx +++ b/src/components/ChatEntry.jsx @@ -29,8 +29,11 @@ ChatEntry.propTypes = { body: PropTypes.string.isRequired, timeStamp: PropTypes.string.isRequired, liked: PropTypes.bool.isRequired, - onToggleLike: PropTypes.func.isRequired + onToggleLike: PropTypes.func, //had to remove isRequired to pass the test from Waves 1 and 2 }; +ChatEntry.defaultProps = { // added dafault props so it stays always defined + onToggleLike: () => {}, +}; export default ChatEntry; \ No newline at end of file