Skip to content
29 changes: 26 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
import React from 'react';
import './App.css';
import chatMessages from './data/messages.json';
import ChatLog from './components/ChatLog'
import { useState } from 'react';

const App = () => {
const [chatData, setChatData] = useState(chatMessages)

const toggleHeart = (id) =>{
setChatData(chatData => chatData.map(message =>{
if (message.id === id) {
return {...message, liked: !message.liked}
} else{
return message;
}
}));
}
const calcHearts = (chatData) =>{
return chatData.reduce((total, message) =>{
return total + message.liked;
},0)
};

const totalHearts = calcHearts(chatData);
return (
<div id="App">
<header>
<h1>Application title</h1>
<h1>Anna's Chat Log</h1>
<h2>{totalHearts} ❤️s</h2>
</header>
<main>
{/* Wave 01: Render one ChatEntry component
Wave 02: Render ChatLog component */}

<ChatLog entries={chatData} onToggleHeart = {toggleHeart}></ChatLog>


</main>
</div>
);
Expand Down
24 changes: 19 additions & 5 deletions src/components/ChatEntry.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
import React from 'react';
import './ChatEntry.css';
import PropTypes from 'prop-types';
import TimeStamp from './TimeStamp';



const ChatEntry = (props) => {
//const [Liked, setLiked] = useState(props.liked);

const heartColor = props.liked ? '❤️' : '🤍';

return (
<div className="chat-entry local">
<h2 className="entry-name">Replace with name of sender</h2>
<h2 className="entry-name">{props.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>{props.body}</p>
<p className="entry-time">
<TimeStamp time={props.timeStamp}/>
</p>
<button className="like" onClick={()=> props.onToggleHeart(props.id)}>{heartColor}</button>
</section>
</div>
);
};

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

export default ChatEntry;
14 changes: 7 additions & 7 deletions src/components/ChatEntry.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import "@testing-library/jest-dom/extend-expect";
import ChatEntry from "./ChatEntry";
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(
<ChatEntry
Expand All @@ -14,15 +14,15 @@ describe("Wave 01: ChatEntry", () => {
);
});

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();
});
});
40 changes: 40 additions & 0 deletions src/components/ChatLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import './ChatLog.css';
import PropTypes from 'prop-types';
import ChatEntry from './ChatEntry';


const ChatLog = (props) =>{
const entries = props.entries;
const chatEntry = entries.map(entry =>{
return (
<ChatEntry
id={entry.id}
sender={entry.sender}
body={entry.body}
timeStamp={entry.timeStamp}
liked={entry.liked}
onToggleHeart={props.onToggleHeart}
/>
);
});
return (
<section>
{chatEntry}
</section>
)
};

ChatLog.prototype = {
entries:PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
sender: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
timeStamp: PropTypes.string.isRequired,
liked: PropTypes.bool,
})
)

}
export default ChatLog;
48 changes: 24 additions & 24 deletions src/components/ChatLog.test.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import React from "react";
import "@testing-library/jest-dom/extend-expect";
import ChatLog from "./ChatLog";
import { render, screen } from "@testing-library/react";
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import ChatLog from './ChatLog';
import { render, screen } from '@testing-library/react';

const LOG = [
{
sender: "Vladimir",
body: "why are you arguing with me",
timeStamp: "2018-05-29T22:49:06+00:00",
sender: 'Vladimir',
body: 'why are you arguing with me',
timeStamp: '2018-05-29T22:49:06+00:00',
},
{
sender: "Estragon",
body: "Because you are wrong.",
timeStamp: "2018-05-29T22:49:33+00:00",
sender: 'Estragon',
body: 'Because you are wrong.',
timeStamp: '2018-05-29T22:49:33+00:00',
},
{
sender: "Vladimir",
body: "because I am what",
timeStamp: "2018-05-29T22:50:22+00:00",
sender: 'Vladimir',
body: 'because I am what',
timeStamp: '2018-05-29T22:50:22+00:00',
},
{
sender: "Estragon",
body: "A robot.",
timeStamp: "2018-05-29T22:52:21+00:00",
sender: 'Estragon',
body: 'A robot.',
timeStamp: '2018-05-29T22:52:21+00:00',
},
{
sender: "Vladimir",
body: "Notabot",
timeStamp: "2019-07-23T22:52:21+00:00",
sender: 'Vladimir',
body: 'Notabot',
timeStamp: '2019-07-23T22:52:21+00:00',
},
];

describe("Wave 02: ChatLog", () => {
describe('Wave 02: ChatLog', () => {
beforeEach(() => {
render(<ChatLog entries={LOG} />);
});

test("renders without crashing and shows all the names", () => {
test('renders without crashing and shows all the names', () => {
[
{
name: "Vladimir",
name: 'Vladimir',
numChats: 3,
},
{
name: "Estragon",
name: 'Estragon',
numChats: 2,
},
].forEach((person) => {
Expand All @@ -56,7 +56,7 @@ describe("Wave 02: ChatLog", () => {
});
});

test("renders an empty list without crashing", () => {
test('renders an empty list without crashing', () => {
const element = render(<ChatLog entries={[]} />);
expect(element).not.toBeNull();
});
Expand Down