Skip to content

Commit

Permalink
feat(pagination): Pagination of Articles
Browse files Browse the repository at this point in the history
- Get the total number of articles
- Define the number of pages
- Create pagination component
- Write tests for pagination of articles

[Delivers #161966576]
  • Loading branch information
TeamoreA committed Jan 25, 2019
1 parent ab04d1a commit 8578fdd
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 15 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
"enzyme-adapter-react-16": "^1.7.1",
"expect": "^23.6.0",
"fetch-mock": "^7.3.0",
"moment": "^2.24.0",
"moxios": "^0.4.0",
"nock": "^10.0.6",
"node-fetch": "^2.3.0",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-addons-test-utils": "^15.6.2",
"react-dom": "^16.7.0",
"react-moment": "^0.8.4",
"react-redux": "^6.0.0",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
Expand Down
80 changes: 80 additions & 0 deletions src/actions/__test__/ArticlesActions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import moxios from 'moxios';
import expect from 'expect';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

import * as actions from '../articleActions';
import * as types from '../actionTypes';

const mock = new MockAdapter(axios);
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

Expand Down Expand Up @@ -133,3 +136,80 @@ describe('Article Actions types', () => {
});
});
});


describe('Pagination', () => {
afterEach = (
() => mock.restore()
);

it('should create an action to get articles count', () => {
const userData = {
email: 'jake@jakecom',
password: '@jakejake254',
};
mock.onGet('https://ah-technocrats.herokuapp.com/api/articles/').replyOnce(200,
{
user: {
token: 'token',
},
});

const expectedActions = [
{
type: types.PAGE,
request: userData,
},
{
type: types.FETCH_ARTICLES_FAILS,
successfulMessage: {
user: {
token: 'token',
},
},
},
];
const store = mockStore({ userData: {} });
store.dispatch(actions.pageData(userData)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});

describe('Get pagintaed data', () => {
afterEach = (
() => mock.restore()
);

it('should create an action to get articles count', () => {
const userData = {
email: 'jake@jakecom',
password: '@jakejake254',
};
mock.onGet('https://ah-technocrats.herokuapp.com/api/articles/?limit=10&offset=10').replyOnce(200,
{
user: {
token: 'token',
},
});

const expectedActions = [
{
type: types.PAGE,
request: userData,
},
{
type: types.FETCH_ARTICLES_FAILS,
successfulMessage: {
user: {
token: 'token',
},
},
},
];
const store = mockStore({ userData: {} });
store.dispatch(actions.getPage(userData)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
});
3 changes: 3 additions & 0 deletions src/actions/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ export const FETCH_ARTICLES_FAILS = 'FETCH_ARTICLES';
export const LOGIN_ACTION = 'LOGIN_ACTION';
export const LOGIN_SUCCESSFUL = 'LOGIN_SUCCESSFUL';
export const LOGIN_REJECTED = 'LOGIN_REJECTED';
export const UPDATED_ARTICLES = 'UPDATED_ARTICLES';
export const PAGE = 'GET_PAGES_COUNT';
export const NEXT_PAGE = 'NEXT_PAGE';
38 changes: 35 additions & 3 deletions src/actions/articleActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from 'axios';
import { FETCH_ARTICLES, FETCH_ARTICLES_SUCCESS, FETCH_ARTICLES_FAILS } from './actionTypes';
import {
FETCH_ARTICLES, FETCH_ARTICLES_SUCCESS, FETCH_ARTICLES_FAILS, PAGE, NEXT_PAGE,
} from './actionTypes';

export const fetchAllArticles = () => (
{
Expand All @@ -14,20 +16,50 @@ export const fetchArticlesSuccess = payload => (
}
);

export const fetchPagesCount = payload => (
{
type: PAGE,
payload,
}
);

export const fetchArticleFails = errorMessage => (
{
type: FETCH_ARTICLES_FAILS,
errorMessage,
}
);
export const getNextPage = () => (
{
type: NEXT_PAGE,
}
);


export function fetchArticles() {
return (dispatch) => {
dispatch(fetchAllArticles());
return axios.get('https://ah-technocrats.herokuapp.com/api/articles/')
return axios.get('https://ah-technocrats.herokuapp.com/api/articles/?limit=10&offset=00')
.then(payload => dispatch(fetchArticlesSuccess(payload.data.results.articles)))
.catch(errorMessage => dispatch(fetchArticleFails(errorMessage)));
};
}

export default fetchArticles;
export function pageData() {
return (dispatch) => {
dispatch(fetchAllArticles());
return axios.get('https://ah-technocrats.herokuapp.com/api/articles/')
.then(payload => dispatch(fetchPagesCount(payload.data.count)))
.catch(errorMessage => dispatch(fetchArticleFails(errorMessage)));
};
}

export function getPage(page) {
const newPage = page * 10 - 10;
return (dispatch) => {
dispatch(getNextPage());
return axios.get(`https://ah-technocrats.herokuapp.com/api/articles/?limit=10&offset=${newPage}`)
.then(payload => dispatch(fetchArticlesSuccess(payload.data.results.articles)))
.catch(errorMessage => dispatch(fetchArticleFails(errorMessage)));
};
}
40 changes: 38 additions & 2 deletions src/components/Articles/AllArticlesComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React from 'react';
import { Container } from 'semantic-ui-react';
import { Container, Pagination } from 'semantic-ui-react';
import { PropTypes } from 'prop-types';
import SingleArticleComponent from './SingleArticleComponent';

const AllArticlesComponent = ({ articles }) => (
let number;

const AllArticlesComponent = ({
articles, pagination, getNewPage,
}) => (
<Container>
<div className="space">
<div className="ui header medium">
Expand All @@ -19,14 +23,46 @@ const AllArticlesComponent = ({ articles }) => (
))}
</div>
</div>
<div className="small-space pagination-button">
<div className="ui pagination menu ">
<Pagination
boundaryRange={0}
defaultActivePage={1}
ellipsisItem={null}
firstItem={null}
lastItem={null}
siblingRange={1}
onClick={(e) => {
number = e.target.innerText;
return getNewPage(e.target.innerText);
}}
totalPages={Math.ceil(pagination / 10)}
/>
</div>
</div>
<div className="small-margin tagline">
<span>
Page
{' '}
{number}
{' '}
of
{' '}
{Math.ceil(pagination / 10)}
</span>
</div>

</div>
<div className="two wide column" />

</div>
</Container>
);

AllArticlesComponent.propTypes = {
articles: PropTypes.shape().isRequired,
pagination: PropTypes.number.isRequired,
getNewPage: PropTypes.func.isRequired,
};

export default AllArticlesComponent;
8 changes: 8 additions & 0 deletions src/components/Articles/Articles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@
margin-top: 20px;
}

.pagination-button {
margin-bottom: 20px;
}

.space-bottom {
margin-bottom:50px
}
Expand Down Expand Up @@ -224,3 +228,7 @@ div [class*="right float"] {
a.red{
color:#67bd45!important;
}

.cursor {
cursor: pointer;
}
31 changes: 23 additions & 8 deletions src/components/Articles/ArticlesContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
Segment,
} from 'semantic-ui-react';

import fetchArticlesAct from '../../actions/articleActions';
import { fetchArticles, pageData, getPage } from '../../actions/articleActions';
import Footer from '../Footer/Footer';
import SideBarMenu from '../Menu/Menu';
import AllArticlesComponent from './AllArticlesComponent';
Expand All @@ -21,19 +21,25 @@ export class ArticleContainer extends React.Component {
}

componentWillMount() {
const { fetchArticles } = this.props;
fetchArticles();
const { fetchAllArticles, fetchpageData } = this.props;
fetchAllArticles();
fetchpageData();
}


render() {
const { articles } = this.props;
const { articles, pagination, getNewPage } = this.props;
return (
<React.Fragment>
<Header />
<Sidebar.Pushable as={Segment} attached="bottom">
<SideBarMenu />
<Sidebar.Pusher id="pusher" className="pusher-height">
<AllArticlesComponent articles={articles} />
<AllArticlesComponent
articles={articles}
pagination={pagination}
getNewPage={getNewPage}
/>
</Sidebar.Pusher>
</Sidebar.Pushable>
<Footer />
Expand All @@ -43,19 +49,28 @@ export class ArticleContainer extends React.Component {
}

ArticleContainer.propTypes = {
fetchArticles: PropTypes.func,
fetchAllArticles: PropTypes.func,
fetchpageData: PropTypes.func,
getNewPage: PropTypes.func.isRequired,
articles: PropTypes.arrayOf(PropTypes.object),
pagination: PropTypes.arrayOf(PropTypes.object).isRequired,
};

ArticleContainer.defaultProps = {
fetchArticles: () => {},
fetchAllArticles: () => {},
fetchpageData: () => {},
articles: [],
};

const mapStateToProps = state => ({
articles: state.articles.items,
pagination: state.articles.count,
// set state for article data
articleData: state.articles,
});

export default connect(mapStateToProps, {
fetchArticles: fetchArticlesAct,
fetchAllArticles: fetchArticles,
fetchpageData: pageData,
getNewPage: getPage,
})(ArticleContainer);
1 change: 0 additions & 1 deletion src/components/Articles/SingleArticleComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const SingleArticleComponent = ({ article }) => (
<div className="excerpt">
{article.description}
<br />
{article.body}
</div>
<div className="small-margin tagline">
<span className="date">{article.created_at}</span>
Expand Down
2 changes: 2 additions & 0 deletions src/reducers/__tests__/ArticlesReducer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import articleReducer from '../articleReducer';
import * as types from '../../actions/actionTypes';

const initialState = {
count: 0,
items: [],
item: {},
};
Expand All @@ -28,6 +29,7 @@ describe('article reducer', () => {
payload,
};
const expectedState = {
count: 0,
item: {},
items: payload,
};
Expand Down
Loading

0 comments on commit 8578fdd

Please sign in to comment.