Skip to content

Commit

Permalink
feat(pagination): add article pagination
Browse files Browse the repository at this point in the history
 - add article pagination
 - Revise article like count prop validation from array to object
   since it has been modified in the back end

[starts #165273536]
  • Loading branch information
Arthur Kalule committed Jun 20, 2019
1 parent da27459 commit 3c51443
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 11 deletions.
6 changes: 6 additions & 0 deletions src/components/ArticlePagination/ArticlePagination.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.article-pagination {
display: flex;
flex-direction: row;
justify-content: space-around;
margin: 1rem;
}
49 changes: 49 additions & 0 deletions src/components/ArticlePagination/ArticlePagination.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';

import { mount } from 'enzyme';

import ArticlePagination from 'components/ArticlePagination';
import Button from 'components/Button';

describe('ArticlePagination', () => {
const props = {
previous: null,
next: 'next',
paginate: jest.fn(),
count: 20,
};
let wrapper = mount(<ArticlePagination {...props} />);

it('should render without crushing', () => {
expect(wrapper).toMatchSnapshot();
});

const previousButton = wrapper.find(Button).at(0);
const nextButton = wrapper.find(Button).at(1);

it('should disable buttons if null', () => {
expect(previousButton.props().disabled).toBe(true);
expect(nextButton.props().disabled).toBe(false);
});


it('should call the paginate function on button click', () => {
const event = {
target: {
type: 'button',
name: '',
},
preventDefault: jest.fn(),
};

nextButton.simulate('click', event);
expect(props.paginate).toBeCalled();
previousButton.simulate('click', event);


props.previous = 'previous';
wrapper = mount(<ArticlePagination {...props} />);
wrapper.find(Button).at(0).simulate('click', event);
expect(props.paginate).toBeCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ArticlePagination should render without crushing 1`] = `
<ArticlePagination
count={20}
next="next"
paginate={[MockFunction]}
previous={null}
>
<div
className="article-pagination"
>
<Button
btnClass="fa fa-backward"
btnEvent={[Function]}
btnName=""
disabled={true}
>
<button
className="fa fa-backward"
disabled={true}
onClick={[Function]}
type="button"
/>
</Button>
<span>
1
/
2
pages
</span>
<Button
btnClass="fa fa-forward"
btnEvent={[Function]}
btnName=""
disabled={false}
>
<button
className="fa fa-forward"
disabled={false}
onClick={[Function]}
type="button"
/>
</Button>
</div>
</ArticlePagination>
`;
65 changes: 65 additions & 0 deletions src/components/ArticlePagination/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// react
import React from 'react';

// Proptypes for validation
import PropTypes from 'prop-types';

// stylesheets
import './ArticlePagination.scss';

// Button component
import Button from 'components/Button';

const ArticlePagination = ({
previous, next, paginate, count, page
}) => {
const pages = Math.ceil(count / 10);
return (

<div className="article-pagination">
<Button
btnName=""
btnClass="fa fa-backward"
btnEvent={(e) => {
e.preventDefault();
paginate(previous, -1);
}}
disabled={!previous}
/>
<span>
{page || 1}
{' '}
{'/'}
{' '}
{pages}
{' '}
{'pages '}
</span>
<Button
btnName=""
btnClass="fa fa-forward"
btnEvent={(e) => {
e.preventDefault();
paginate(next, 1);
}}
disabled={!next}
/>
</div>
);
};

ArticlePagination.defaultProps = {
next: null,
previous: null,
};

ArticlePagination.propTypes = {
next: PropTypes.string,
page: PropTypes.number.isRequired,
count: PropTypes.number.isRequired,
previous: PropTypes.string,
paginate: PropTypes.func.isRequired,
};


export default ArticlePagination;
9 changes: 8 additions & 1 deletion src/components/Button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import PropTypes from 'prop-types';
const Button = (props) => {
// Destructure props
const {
btnClass, btnName, btnEvent,
btnClass, btnName, btnEvent, disabled
} = props;

return (
Expand All @@ -16,14 +16,21 @@ const Button = (props) => {
className={btnClass}
type="button"
onClick={btnEvent}
disabled={disabled}
>
{btnName}
</button>
);
};

// Default proptypes
Button.defaultProps = {
disabled: false,
};

// Props validation
Button.propTypes = {
disabled: PropTypes.bool,
btnName: PropTypes.string.isRequired,
btnClass: PropTypes.string.isRequired,
btnEvent: PropTypes.func.isRequired,
Expand Down
2 changes: 2 additions & 0 deletions src/components/Login/__snapshots__/login.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ exports[`login component matches the snapshot 1`] = `
btnClass="login__button"
btnEvent={[Function]}
btnName="Login"
disabled={false}
/>
<i
className="login__forgot__pwd"
Expand Down Expand Up @@ -129,6 +130,7 @@ exports[`login component should render without crsshing 1`] = `
btnClass="login__button"
btnEvent={[Function]}
btnName="Login"
disabled={false}
/>
<i
className="login__forgot__pwd"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ exports[`SignupForm Component matches the snapshot 1`] = `
btnClass="signupForm__button"
btnEvent={[Function]}
btnName="Sign up"
disabled={false}
id="signupForm__button"
/>
</form>
Expand Down
15 changes: 15 additions & 0 deletions src/pages/Landing/Landing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,19 @@ describe('Article Column', () => {
const component = shallow(<ArticleColumn columnTitle="Most Recent" articles={[mockArticle()]} />);
expect(component.exists()).toBe(true);
});

it('should call the get articles function', () => {
const getArticlesFn = jest.fn();
const props = {
next: 'nexturl',
previous: 'previousurl',
count: 10,
articles: [],
getArticles: getArticlesFn,

};
const wrapper = shallow(<LandingPage {...props} />);
wrapper.instance().paginate();
expect(getArticlesFn).toBeCalled();
});
});
63 changes: 57 additions & 6 deletions src/pages/Landing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, { Component } from 'react';

// third-party libraries
import { ToastContainer } from 'react-toastify';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

Expand All @@ -15,19 +14,44 @@ import Navbar from 'components/NavBar';
import Footer from 'components/Footer';
import Loader from 'components/Loader';
import { getAllArticles } from 'store/actions/articleActions';
import ArticlePagination from 'components/ArticlePagination';
import ArticleColumn from './ArticleColumn';


export class LandingPage extends Component {
constructor(props) {
super(props);
this.state = {
page: 1,
};
}

componentDidMount() {
const { getArticles } = this.props;

// fetch articles
getArticles();
}


paginate = (apiCallUrl, Increment) => {
const { getArticles } = this.props;
const { page } = this.state;
this.setState({
page: page + Increment
});

getArticles(apiCallUrl);
};


render() {
const { articles, isFetching } = this.props;
const {
articles, isFetching, previous, count, next
} = this.props;
const { page } = this.state;


return (

// Navbar
Expand Down Expand Up @@ -65,7 +89,18 @@ export class LandingPage extends Component {
<ArticleColumn columnTitle="Most Popular" articles={articles} />
<ArticleColumn columnTitle="Most Liked" articles={articles} />
<ArticleColumn columnTitle="Most Recent" articles={articles} />

</div>
<div>
<ArticlePagination
paginate={this.paginate}
next={next}
previous={previous}
count={count}
page={page}
/>
</div>

<Footer />
</div>
</div>
Expand All @@ -75,6 +110,9 @@ export class LandingPage extends Component {

// proptype validation
LandingPage.propTypes = {
next: PropTypes.string,
count: PropTypes.number,
previous: PropTypes.string,
getArticles: PropTypes.func,
isFetching: PropTypes.bool,
articles: PropTypes.arrayOf(PropTypes.shape({
Expand Down Expand Up @@ -105,6 +143,9 @@ LandingPage.propTypes = {

LandingPage.defaultProps = {
articles: [],
next: null,
previous: null,
count: 0,
isFetching: false,
getArticles: () => { },
};
Expand All @@ -115,15 +156,25 @@ LandingPage.defaultProps = {
* @return {articles} - An aray of articles
*
*/
const mapStateToProps = state => state.articles;
// const mapStateToProps = state => state.articles;
const mapStateToProps = (state) => {
const {
next, count, previous, articles
} = state.articles;
return {
next, count, previous, articles
};
};

/**
* This is a function to pass the state as a prop.
* @param {dispatch} - The dispatch function
* @return {getArticles} - The thunk to fetch articles
*
*/
const mapDispatchToProps = dispatch => bindActionCreators({
getArticles: () => getAllArticles(),
}, dispatch);

const mapDispatchToProps = dispatch => ({
getArticles(apiCallUrl) { dispatch(getAllArticles(apiCallUrl)); },
});

export default connect(mapStateToProps, mapDispatchToProps)(LandingPage);
Loading

0 comments on commit 3c51443

Please sign in to comment.