Skip to content

Commit 6cb7b6d

Browse files
committed
2011-useClickTracker-HoaxView
1 parent 636ec90 commit 6cb7b6d

File tree

2 files changed

+86
-58
lines changed

2 files changed

+86
-58
lines changed

frontend/src/components/HoaxView.js

+63-47
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,80 @@
1-
import React, { Component } from 'react';
1+
import React, { useRef } from 'react';
22
import ProfileImageWithDefault from './ProfileImageWithDefault';
33
import { format } from 'timeago.js';
44
import { Link } from 'react-router-dom';
55
import { connect } from 'react-redux';
6+
import useClickTracker from '../shared/useClickTracker';
67

7-
class HoaxView extends Component {
8-
render() {
9-
const { hoax, onClickDelete } = this.props;
10-
const { user, date } = hoax;
11-
const { username, displayName, image } = user;
12-
const relativeDate = format(date);
13-
const attachmentImageVisible =
14-
hoax.attachment && hoax.attachment.fileType.startsWith('image');
8+
const HoaxView = (props) => {
9+
const actionArea = useRef();
10+
const dropDownVisible = useClickTracker(actionArea);
11+
const { hoax, onClickDelete } = props;
12+
const { user, date } = hoax;
13+
const { username, displayName, image } = user;
14+
const relativeDate = format(date);
15+
const attachmentImageVisible =
16+
hoax.attachment && hoax.attachment.fileType.startsWith('image');
1517

16-
const ownedByLoggedInUser = user.id === this.props.loggedInUser.id;
17-
return (
18-
<div className="card p-1">
19-
<div className="d-flex">
20-
<ProfileImageWithDefault
21-
className="rounded-circle m-1"
22-
width="32"
23-
height="32"
24-
image={image}
25-
/>
26-
<div className="flex-fill m-auto pl-2">
27-
<Link to={`/${username}`} className="list-group-item-action">
28-
<h6 className="d-inline">
29-
{displayName}@{username}
30-
</h6>
31-
</Link>
32-
<span className="text-black-50"> - </span>
33-
<span className="text-black-50">{relativeDate}</span>
34-
</div>
35-
{ownedByLoggedInUser && (
36-
<button
37-
className="btn btn-outline-danger btn-sm"
38-
onClick={onClickDelete}
39-
>
40-
<i className="far fa-trash-alt" />
41-
</button>
42-
)}
18+
const ownedByLoggedInUser = user.id === props.loggedInUser.id;
19+
20+
let dropDownClass = 'p-0 shadow dropdown-menu';
21+
if (dropDownVisible) {
22+
dropDownClass += ' show';
23+
}
24+
25+
return (
26+
<div className="card p-1">
27+
<div className="d-flex">
28+
<ProfileImageWithDefault
29+
className="rounded-circle m-1"
30+
width="32"
31+
height="32"
32+
image={image}
33+
/>
34+
<div className="flex-fill m-auto pl-2">
35+
<Link to={`/${username}`} className="list-group-item-action">
36+
<h6 className="d-inline">
37+
{displayName}@{username}
38+
</h6>
39+
</Link>
40+
<span className="text-black-50"> - </span>
41+
<span className="text-black-50">{relativeDate}</span>
4342
</div>
44-
<div className="pl-5">{hoax.content}</div>
45-
{attachmentImageVisible && (
46-
<div className="pl-5">
47-
<img
48-
alt="attachment"
49-
src={`/images/attachments/${hoax.attachment.name}`}
50-
className="img-fluid"
43+
{ownedByLoggedInUser && (
44+
<div className="dropdown">
45+
<span
46+
className="btn btn-sm btn-light dropdown-toggle"
47+
data-testid="hoax-actions"
48+
ref={actionArea}
5149
/>
50+
<div className={dropDownClass} data-testid="hoax-action-dropdown">
51+
<button
52+
className="btn btn-outline-danger btn-sm btn-block text-left"
53+
onClick={onClickDelete}
54+
>
55+
<i className="far fa-trash-alt" /> Delete
56+
</button>
57+
</div>
5258
</div>
5359
)}
5460
</div>
55-
);
56-
}
57-
}
61+
<div className="pl-5">{hoax.content}</div>
62+
{attachmentImageVisible && (
63+
<div className="pl-5">
64+
<img
65+
alt="attachment"
66+
src={`/images/attachments/${hoax.attachment.name}`}
67+
className="img-fluid"
68+
/>
69+
</div>
70+
)}
71+
</div>
72+
);
73+
};
5874

5975
const mapStateToProps = (state) => {
6076
return {
61-
loggedInUser: state
77+
loggedInUser: state,
6278
};
6379
};
6480

frontend/src/components/HoaxView.spec.js

+23-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { render } from '@testing-library/react';
2+
import { render, fireEvent } from '@testing-library/react';
33
import HoaxView from './HoaxView';
44
import { MemoryRouter } from 'react-router-dom';
55
import { Provider } from 'react-redux';
@@ -12,7 +12,7 @@ const loggedInStateUser1 = {
1212
displayName: 'display1',
1313
image: 'profile1.png',
1414
password: 'P4ssword',
15-
isLoggedIn: true
15+
isLoggedIn: true,
1616
};
1717

1818
const loggedInStateUser2 = {
@@ -21,7 +21,7 @@ const loggedInStateUser2 = {
2121
displayName: 'display2',
2222
image: 'profile2.png',
2323
password: 'P4ssword',
24-
isLoggedIn: true
24+
isLoggedIn: true,
2525
};
2626

2727
const hoaxWithoutAttachment = {
@@ -31,8 +31,8 @@ const hoaxWithoutAttachment = {
3131
id: 1,
3232
username: 'user1',
3333
displayName: 'display1',
34-
image: 'profile1.png'
35-
}
34+
image: 'profile1.png',
35+
},
3636
};
3737

3838
const hoaxWithAttachment = {
@@ -42,12 +42,12 @@ const hoaxWithAttachment = {
4242
id: 1,
4343
username: 'user1',
4444
displayName: 'display1',
45-
image: 'profile1.png'
45+
image: 'profile1.png',
4646
},
4747
attachment: {
4848
fileType: 'image/png',
49-
name: 'attached-image.png'
50-
}
49+
name: 'attached-image.png',
50+
},
5151
};
5252

5353
const hoaxWithPdfAttachment = {
@@ -57,12 +57,12 @@ const hoaxWithPdfAttachment = {
5757
id: 1,
5858
username: 'user1',
5959
displayName: 'display1',
60-
image: 'profile1.png'
60+
image: 'profile1.png',
6161
},
6262
attachment: {
6363
fileType: 'application/pdf',
64-
name: 'attached.pdf'
65-
}
64+
name: 'attached.pdf',
65+
},
6666
};
6767

6868
const setup = (hoax = hoaxWithoutAttachment, state = loggedInStateUser1) => {
@@ -129,5 +129,17 @@ describe('HoaxView', () => {
129129
const { container } = setup(hoaxWithoutAttachment, loggedInStateUser2);
130130
expect(container.querySelector('button')).not.toBeInTheDocument();
131131
});
132+
it('does not show the dropdown menu when not clicked', () => {
133+
const { queryByTestId } = setup();
134+
const dropDownMenu = queryByTestId('hoax-action-dropdown');
135+
expect(dropDownMenu).not.toHaveClass('show');
136+
});
137+
it('shows the dropdown menu after clicking the indicator', () => {
138+
const { queryByTestId } = setup();
139+
const indicator = queryByTestId('hoax-actions');
140+
fireEvent.click(indicator);
141+
const dropDownMenu = queryByTestId('hoax-action-dropdown');
142+
expect(dropDownMenu).toHaveClass('show');
143+
});
132144
});
133145
});

0 commit comments

Comments
 (0)