Skip to content

Commit

Permalink
Implement actions for liking and disliking an article
Browse files Browse the repository at this point in the history
Edit toast error to display error message from server
  • Loading branch information
ibrahimalausa committed Mar 4, 2019
1 parent 27fee6d commit 8913772
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 22 deletions.
3 changes: 2 additions & 1 deletion src/actions/articleReactionsAction/actionTypes.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const actionTypes = {
TOTAL_REACTIONS_LOADING: 'TOTAL_REACTIONS_LOADING',
TOTAL_REACTIONS_SUCCESS: 'TOTAL_REACTIONS_SUCCESS',
TOTAL_REACTIONS_FAILURE: 'TOTAL_REACTIONS_FAILURE'
TOTAL_REACTIONS_FAILURE: 'TOTAL_REACTIONS_FAILURE',
ARTICLE_REACTION_SUCCESS: 'ARTICLE_REACTION_SUCCESS'
};

export default actionTypes;
31 changes: 24 additions & 7 deletions src/actions/articleReactionsAction/articleReactionActions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import moxios from 'moxios';
import * as actions from './index';
import actionTypes from './actionTypes';
import axios from '../../helpers/axiosHelper/totalArticleReactions';
import helper from '../../helpers/axiosHelper/updateArticleReaction';
import { articleId, totalReactions as payload } from '../../mockData/readArticle';
import { error, reactionType, validReaction } from '../../mockData/articleReactions';

const error = {
response: {
data: { success: false, message: 'Value must be a UUID' }
}
};

const { TOTAL_REACTIONS_FAILURE, TOTAL_REACTIONS_LOADING, TOTAL_REACTIONS_SUCCESS } = actionTypes;
const {
TOTAL_REACTIONS_FAILURE,
TOTAL_REACTIONS_LOADING,
TOTAL_REACTIONS_SUCCESS,
ARTICLE_REACTION_SUCCESS
} = actionTypes;

const mockStore = configureStore([thunk]);
const store = mockStore({ totalReactions: {} });
Expand Down Expand Up @@ -58,4 +59,20 @@ describe('actions to fetch number of likes and dislikes', () => {
payload: payload.data
});
});

it('should return an action type ARTICLE_REACTION_SUCCESS when the operation is successful', () => {
expect(actions.articleReactionSuccess(reactionType)).toEqual({
type: ARTICLE_REACTION_SUCCESS,
payload: reactionType
});
});
it('should dispatch success action type and response as payload', async () => {
helper.updateArticleReaction = jest.fn().mockResolvedValue(validReaction);
await actions.articleReaction(articleId, reactionType)(dispatch);
expect(dispatch).toBeCalledTimes(1);
expect(dispatch).toBeCalledWith({
type: ARTICLE_REACTION_SUCCESS,
payload: reactionType
});
});
});
30 changes: 28 additions & 2 deletions src/actions/articleReactionsAction/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { toast } from 'react-toastify';
import actionTypes from './actionTypes';
import axios from '../../helpers/axiosHelper/updateArticleReaction';
import axiosHelper from '../../helpers/axiosHelper/totalArticleReactions';
import triggerLoading from '../authAction/loading';

const { TOTAL_REACTIONS_FAILURE, TOTAL_REACTIONS_LOADING, TOTAL_REACTIONS_SUCCESS } = actionTypes;
const {
TOTAL_REACTIONS_FAILURE,
TOTAL_REACTIONS_LOADING,
TOTAL_REACTIONS_SUCCESS,
ARTICLE_REACTION_SUCCESS
} = actionTypes;

const totalReactionsSuccess = payload => ({
type: TOTAL_REACTIONS_SUCCESS,
Expand All @@ -24,4 +31,23 @@ const getTotalReactions = articleId => async (dispatch) => {
}
};

export { getTotalReactions, totalReactionsFailure, totalReactionsSuccess };
const articleReactionSuccess = reactionType => ({
type: ARTICLE_REACTION_SUCCESS,
payload: reactionType
});

const articleReaction = (articleId, reactionType) => async (dispatch) => {
try {
await axios.updateArticleReaction(articleId, reactionType);
dispatch(articleReactionSuccess(reactionType));
} catch (error) {
toast.error(error.response.data.message);
}
};
export {
getTotalReactions,
totalReactionsFailure,
totalReactionsSuccess,
articleReaction,
articleReactionSuccess
};
28 changes: 17 additions & 11 deletions src/components/widgets/ReactionIcons.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { Component, Fragment } from 'react';
import { Icon } from 'semantic-ui-react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { getTotalReactions } from '../../actions/articleReactionsAction';
import { dateFormatter } from '../../helpers/articleInfoFormatter';
import { getTotalReactions, articleReaction } from '../../actions/articleReactionsAction';

class ReactionIcons extends Component {
state = {
Expand All @@ -15,24 +15,28 @@ class ReactionIcons extends Component {
this.props.getTotalReactions(this.props.selectedArticleId);
};

likeArticle = (outline = 'outline') => {
createLikeIcon = (outline = 'outline') => {
const name = outline.length === 0 ? 'thumbs up' : `thumbs up ${outline}`;
return (
<i role="presentation">
<i role="presentation" onClick={this.createReaction.bind(this, 'like')}>
<Icon disabled link size="small" fitted name={`thumbs up ${name}`} />
</i>
);
};

dislikeArticle = (outline = 'outline') => {
createDislikeIcon = (outline = 'outline') => {
const name = outline.length === 0 ? 'thumbs down' : `thumbs down ${outline}`;
return (
<i role="presentation">
<i role="presentation" onClick={this.createReaction.bind(this, 'dislike')}>
<Icon disabled link size="small" fitted name={`thumbs down ${name}`} />
</i>
);
};

createReaction = (reaction) => {
this.props.articleReaction(this.props.selectedArticleId, reaction);
};

render() {
const { date, numberofcomments, totalArticleReactions } = this.props;
const { likes, dislikes } = totalArticleReactions.successResponse;
Expand All @@ -43,10 +47,11 @@ class ReactionIcons extends Component {
<Icon disabled link size="small" fitted name="time">
<span>{dateFormatter(date)}</span>
</Icon>
{likeClicked ? this.likeArticle(outline) : this.likeArticle()}
<span>{likes}</span>
{dislikeClicked ? this.dislikeArticle(outline) : this.dislikeArticle()}
<span>{dislikes}</span>
<i className="dates">{date}</i>
{likeClicked ? this.createLikeIcon(outline) : this.createLikeIcon()}
{likes}
{dislikeClicked ? this.createDislikeIcon(outline) : this.createDislikeIcon()}
{dislikes}
<i className="articleComment">
<Icon disabled link size="small" fitted name="comments outline" />
<span>{numberofcomments}</span>
Expand All @@ -65,7 +70,8 @@ ReactionIcons.propTypes = {
numberofcomments: PropTypes.number,
selectedArticleId: PropTypes.string.isRequired,
getTotalReactions: PropTypes.func.isRequired,
totalArticleReactions: PropTypes.object.isRequired
totalArticleReactions: PropTypes.object.isRequired,
articleReaction: PropTypes.func.isRequired
};

const mapStateToProps = state => ({
Expand All @@ -75,5 +81,5 @@ const mapStateToProps = state => ({
export { ReactionIcons as ReactionIconsPage };
export default connect(
mapStateToProps,
{ getTotalReactions }
{ getTotalReactions, articleReaction }
)(ReactionIcons);
4 changes: 3 additions & 1 deletion src/components/widgets/test/ReactionIcons.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ describe('Test for reaction Icons', () => {
});

it('should have initial state of false and change when clicked ', () => {
const articleReaction = jest.fn();
const wrapper = shallow(
<ReactionIconsPage
getTotalReactions={getTotalReactions}
selectedArticleId={props.match.params.title}
totalArticleReactions={props.totalArticleReactions}
articleReaction={articleReaction}
/>
);

expect(wrapper).toMatchSnapshot();

wrapper.instance().createReaction();
expect(wrapper.state().dislikeClicked).toEqual(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ exports[`Test for reaction Icons should have initial state of false and change w
</span>
</Icon>
<i
onClick={[Function]}
role="presentation"
>
<Icon
Expand All @@ -30,6 +31,7 @@ exports[`Test for reaction Icons should have initial state of false and change w
2
</span>
<i
onClick={[Function]}
role="presentation"
>
<Icon
Expand Down Expand Up @@ -77,6 +79,7 @@ exports[`Test for reaction Icons should have initial state of false and change w
</span>
</Icon>
<i
onClick={[Function]}
role="presentation"
>
<Icon
Expand All @@ -92,6 +95,7 @@ exports[`Test for reaction Icons should have initial state of false and change w
2
</span>
<i
onClick={[Function]}
role="presentation"
>
<Icon
Expand Down
23 changes: 23 additions & 0 deletions src/helpers/axiosHelper/test/updateArticleReaction.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axios from 'axios';
import verify from '../updateArticleReaction';
import { articleId } from '../../../mockData/readArticle';

const result = {
success: true,
message: 'Operation Successful'
};

const reactionType = 'dislike';

describe('Test for create article reaction function', () => {
it('should return a response object', async () => {
const axiosPut = axios.put;
const urlPath = `${
process.env.API_BASE_URL
}/articles/reaction/${articleId}/?reaction=${reactionType}`;
axios.put = jest.fn(() => Promise.resolve(result));
const response = await verify.updateArticleReaction(urlPath);
expect(response).toEqual(result);
axios.put = axiosPut;
});
});
12 changes: 12 additions & 0 deletions src/helpers/axiosHelper/updateArticleReaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import axios from 'axios';
import { config } from '../jwt';

const updateArticleReaction = (articleId, reactionType) => {
const url = `${
process.env.API_BASE_URL
}/articles/reaction/${articleId}/?reaction=${reactionType}`;
const response = axios.put(url, { articleId, reactionType }, config);
return response;
};

export default { updateArticleReaction };
12 changes: 12 additions & 0 deletions src/mockData/articleReactions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const error = {
response: {
data: { success: false, message: 'Value must be a UUID' }
}
};
const reactionType = 'dislike';

const validReaction = {
success: true,
message: 'Operation Successful'
};
export { error, reactionType, validReaction };

0 comments on commit 8913772

Please sign in to comment.