Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
212d3ac
add ChatEntry component to App component
abbymachines Jun 28, 2023
217c191
add testMessage variable
abbymachines Jun 28, 2023
fadad84
add testMessage to ChatEntry component in App component as a prop
abbymachines Jun 28, 2023
89c1aa3
start adding props to ChatEntry
abbymachines Jun 28, 2023
b887bb1
import components into each other
abbymachines Jun 28, 2023
46f609d
pass props down to ChatEntry
abbymachines Jun 28, 2023
9b4c2ef
format props in ChatEntry
abbymachines Jun 28, 2023
0fdf48b
change ChatEntry props structure to match tests
abbymachines Jun 28, 2023
e7fb86c
implement basic chatlog component
abbymachines Jun 28, 2023
be6c5b1
apply CSS style to ChatLog component
abbymachines Jun 28, 2023
1016d29
import ChatEntry.css to ChatEntry component
abbymachines Jun 28, 2023
d7eda22
implement local and remote styles with setMessageLocation helper func…
abbymachines Jun 28, 2023
23a80ee
implement onClick function call to ChatEntry heart button
abbymachines Jun 28, 2023
c4dcbf1
last commits for the night
abbymachines Jun 28, 2023
39c7a8f
remove testMessage from App component
abbymachines Jun 28, 2023
3f7c571
remove messageComponents from App component
abbymachines Jun 28, 2023
81b070a
clean up App component
abbymachines Jun 28, 2023
e6cc60e
remove state handler from app component
abbymachines Jun 28, 2023
4181066
refactor ChatEntry component, set button click to do a console.log
abbymachines Jun 28, 2023
fac0d81
refactor printLike in ChatEntry component
abbymachines Jun 28, 2023
655a19b
refactor printLike function to be passed from app component as onLike…
abbymachines Jun 28, 2023
94e3ca9
turn onLikeMessage in ChatEntry so that it runs a console.log
abbymachines Jun 29, 2023
e0a534b
remove commented code from ChatEntry component
abbymachines Jun 29, 2023
138a33a
refactor ChatEntry button onClick so it prints event object details
abbymachines Jun 29, 2023
dadeef6
change onClick back to anonymous function
abbymachines Jun 29, 2023
59e80b7
add heartVersion ternary expression to ChatEntry component
abbymachines Jun 29, 2023
0bf2089
refactor ChatEntry to ensure heartVersion ternary is inside of ChatEn…
abbymachines Jun 29, 2023
7815aae
implement state for ChatEntry component
abbymachines Jun 29, 2023
a4c6e57
implement interactive like buttons (with checkboxes anbd moons)
abbymachines Jun 29, 2023
fe3de7c
change emojis to hearts
abbymachines Jun 29, 2023
b2ed277
remove state from like buttons
abbymachines Jun 29, 2023
77ac454
continue building out toggleLiked in App component
abbymachines Jun 29, 2023
80c29f2
got it to workgit add .git add . the state is now updating the data ^…
abbymachines Jun 29, 2023
7303bbb
remove extraneous comments from app component
abbymachines Jun 29, 2023
968a7bc
remove extraneous comments
abbymachines Jun 29, 2023
86ed929
format text for heart counter
abbymachines Jun 29, 2023
1a143c2
refactor to make HeartCounter its own react component
abbymachines Jun 29, 2023
dc42f7e
add button and onClick to HeartCounter component
abbymachines Jun 29, 2023
8bc5806
turn HeartCounter button onClick argument into anonymous arrow function
abbymachines Jun 30, 2023
11375f6
add likeTotal prop to HeartCounter component
abbymachines Jun 30, 2023
9c5a468
implement state handling for likeTotal prop in HeartCounter component
abbymachines Jun 30, 2023
1ea8554
refactor HeartContainer component
abbymachines Jun 30, 2023
03e0096
remove print statement from toggleLiked function in App component
abbymachines Jun 30, 2023
12511c9
remove print statement from ChatEntry component
abbymachines Jun 30, 2023
4d7a8e4
add PropTypes to ChatEntry component
abbymachines Jun 30, 2023
ecf20b6
add PropTypes to ChatLog
abbymachines Jun 30, 2023
6affa1f
add propTypes to LikeButton component
abbymachines Jun 30, 2023
ab27f99
remove printMessage function from HeartCounter component
abbymachines Jun 30, 2023
99a4d52
add proptypes to HeartCounter component
abbymachines Jun 30, 2023
05eb8c5
refactor to set total like count in App component without using an a…
abbymachines Jul 22, 2023
fc817b4
pass messageData as prop to ChatLog component instead of passing down…
abbymachines Jul 22, 2023
72534b7
make wave 3 tests pass by adding 'likes' className to thhe LikeButton…
abbymachines Jul 22, 2023
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
6 changes: 5 additions & 1 deletion src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,8 @@

.purple {
color: purple
}
}

/* button like {

} */
29 changes: 24 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
import React from 'react';
import React, {useState} from 'react';
import './App.css';
import chatMessages from './data/messages.json';
import ChatLog from './components/ChatLog';
import HeartCounter from './components/HeartCounter'
import messagesJSON from './data/messages.json'

const App = () => {
const [messageData, setMessageData] = useState(messagesJSON);

const toggleLiked = (id) => {
const messages = messageData.map((message) => {
if (message.id === id) {
message.liked = !message.liked;

Choose a reason for hiding this comment

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

Here, you're mutating the original message data that's imported on line 5 above. This was actually causing some tests to fail, in conjunction with a few other points mentioned above and below. For React, you'll want to be cautious about such mutations as it can cause unexpected bugs. The best approach to updating data is actually to create new objects, e.g.

  const toggleLiked = (id) => {
    const messages = messageData.map((message) => {
      if (message.id === id) {
        if (message.liked === false) {
          setHeartTotal((prevHeartTotal) => prevHeartTotal + 1);
        } else {
          setHeartTotal((prevHeartTotal) => prevHeartTotal - 1);
        };

        return {
          ...message,
          liked: !message.liked,
        }

      } else {
        return message;
      }
    });

    setMessageData(messages)
}

But better still would be to do this while also leveraging the approach outlined above of reading the previous data passed to a callback function of the state updating function, e.g.

  const toggleLiked = (id) => {
    setMessageData((prevMessages => {
      return prevMessages.map((message) => {
        if (message.id === id) {
          return {
            ...message,
            liked: !message.liked,
          }
        } else {
          return message;
        }
      });
    }));
  }

and then calculating the heart total separately

  const heartTotal = messageData.filter(m => m.liked).length;

}
return message;
});

setMessageData(messages);
}

const totalLikes = messagesJSON.filter(message => message.liked).length;

return (

<div id="App">
<header>
<h1>Application title</h1>
<h1>Chat with Vladimir</h1>
<br></br>
<HeartCounter likeTotal={totalLikes} />
</header>
<main>
{/* Wave 01: Render one ChatEntry component
Wave 02: Render ChatLog component */}
<ChatLog entries={messageData} onLikeMessage={toggleLiked}/>
</main>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions src/components/ChatEntry.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,9 @@ button {

.chat-entry.remote .entry-bubble:hover::before {
background-color: #a9f6f6;
}

/* like styles */
.like {
font-size: small;
}
42 changes: 34 additions & 8 deletions src/components/ChatEntry.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
import React from 'react';
import React, {useState} from 'react';
import './ChatEntry.css';
import PropTypes from 'prop-types';
import TimeStamp from './TimeStamp';
import LikeButton from './LikeButton';

const setMessageLocation = (userName) => {

if (userName === 'Vladimir') {
return 'chat-entry local';
} else if (userName === 'Estragon') {
return 'chat-entry remote';
};

}

const ChatEntry = ({id, sender, body, timeStamp, isLiked, onLikeMessage}) => {

const updateLike = () => {
onLikeMessage(id);
};

const senderLocation = setMessageLocation(sender);

const ChatEntry = (props) => {
return (
<div className="chat-entry local">
<h2 className="entry-name">Replace with name of sender</h2>
<div className={senderLocation}>
<h2 className="entry-name">{sender}</h2>
<section className="entry-bubble">
<p>Replace with body of ChatEntry</p>
<p className="entry-time">Replace with TimeStamp component</p>
<button className="like">🤍</button>
<p>{body}</p>
<p className="entry-time"><TimeStamp time={timeStamp}/></p>

<LikeButton heartCondition={isLiked} updateLike={updateLike}/>
</section>
</div>
);
};

ChatEntry.propTypes = {
//Fill with correct proptypes
id: PropTypes.number.isRequired,
sender: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
timeStamp: PropTypes.string.isRequired,
isLiked: PropTypes.bool.isRequired,
onLikeMessage: PropTypes.func.isRequired,
};

export default ChatEntry;

38 changes: 38 additions & 0 deletions src/components/ChatLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import ChatEntry from './ChatEntry';
import PropTypes from 'prop-types';
import './ChatLog.css';

const ChatLog = ({entries, onLikeMessage}) => {
const chatComponents = entries.map((message) => {
return (
<div key={message.id} >

Choose a reason for hiding this comment

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

Typically, we'd wrap a component in additional JSX if there was a structural or style consideration. Not sure I see either factoring in here, so you could get rid of the additional JSX and just return the ChatEntry component. In general, you'd prefer not adding unnecessary markup.

<ChatEntry
id={message.id}
sender={message.sender}
body={message.body}
timeStamp={message.timeStamp}
isLiked={message.liked}
onLikeMessage={onLikeMessage}
/>
</div>
);
});

return (<section className='chat-log'>
{chatComponents}
</section>)
};

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
})),
onLikeMessage: PropTypes.func.isRequired,
};

export default ChatLog;
19 changes: 19 additions & 0 deletions src/components/HeartCounter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import '../App.css';
import PropTypes from 'prop-types';

const HeartCounter = (props) => {

Choose a reason for hiding this comment

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

I like how you decided to create additional components that are controlled by props. It encourages reusability, which is an important thing in React.


return (
<div>
<p className='heartWidget'>
{props.likeTotal} ❤️s
</p>
</div>
);
};

HeartCounter.propTypes = {
likeTotal: PropTypes.number.isRequired,
};

export default HeartCounter;
17 changes: 17 additions & 0 deletions src/components/LikeButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';

const LikeButton = (props) => {

Choose a reason for hiding this comment

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

Again, I like how you created a new component to abstract the like button and control it with props 👍🏽

if (props.heartCondition) {

Choose a reason for hiding this comment

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

This kind of conditional works fine but is a bit repetitive. It would work well to set the heart emoji based on a condition in a ternary and thereby reduce the amount of JSX:

const LikeButton = (props) => (
  <button className="like" onClick={props.updateLike}>
    {props.heartCondition ? '❤️' : '🤍'}
  </button>
);

return <button onClick={props.updateLike}>❤️</button>

Choose a reason for hiding this comment

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

Some tests are failing because there is a class that the tests are using in order to be selected and fire click events on (see line 24 in App.test.js). If you add the like to the className prop, the tests will be able to select like buttons correct again. I think possibly you might have forgotten to carry it over when creating this component, because it was in the JSX from the start. It would also allow you to apply the style that you added in line 104 of ChatEntry.css (since it's selecting the like class).

} else {
return <button className='like' onClick={props.updateLike}>🤍</button>
};
};

LikeButton.propTypes = {
heartCondition: PropTypes.bool.isRequired,
updateLike: PropTypes.func.isRequired
}

export default LikeButton;