Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pagerCount props #162

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ React.render(<Pagination />, container);
| nextIcon | specifict the default previous icon | ReactNode \| (props: PaginationProps) => ReactNode | |
| jumpPrevIcon | specifict the default previous icon | ReactNode \| (props: PaginationProps) => ReactNode | |
| jumpNextIcon | specifict the default previous icon | ReactNode \| (props: PaginationProps) => ReactNode | |

| pagerCount | show number of pagers | Number | 5 |

## License

Expand Down
1 change: 1 addition & 0 deletions examples/pagerCount.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
placeholder
50 changes: 50 additions & 0 deletions examples/pagerCount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'rc-pagination/assets/index.less';
import Pagination from 'rc-pagination';
import React from 'react';
import ReactDOM from 'react-dom';
import Select from 'rc-select';
import 'rc-select/assets/index.css';

const itemRender = (current, type, element) => {
const hideItems = ['jump-last', 'jump-first'];

if (hideItems.includes(type)) {
return null;
}

return element;
};

function onShowSizeChange(current, pageSize) {
console.log(current, pageSize);
}

ReactDOM.render(
<div>
<p> pageCount = 10, hide prev and next jumpers </p>
<Pagination total={500} itemRender={itemRender} pagerCount={10} showPrevNextJumpers={false} />

<p> Has `showSizeChanger` and `showQuickJumper` </p>
<Pagination
selectComponentClass={Select}
showSizeChanger
onShowSizeChange={onShowSizeChange}
defaultCurrent={3}
total={500}
pagerCount={7}
showQuickJumper
/>

<p> pagerCount less than 3 </p>
<Pagination total={100} pagerCount={1} />

<p> Has `showLessItems` and `pagerCount` </p>
<Pagination total={500} pagerCount={8} showLessItems />

<p> The pagerCount is odd </p>
<Pagination total={200} pagerCount={7} />

<p> The pagerCount is even </p>
<Pagination total={200} pagerCount={8} />
</div>
, document.getElementById('__react-content'));
13 changes: 12 additions & 1 deletion src/Pager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,26 @@ const Pager = (props) => {
props.onKeyPress(e, props.onClick, props.page);
};

let pageType = 'page';
if (props.last) {
pageType = 'jump-last';
}
if (props.first) {
pageType = 'jump-first';
}

const itemNode = props.itemRender(props.page, pageType, <a>{props.page}</a>);

return (
itemNode === null ? null :
<li
title={props.showTitle ? props.page : null}
className={cls}
onClick={handleClick}
onKeyPress={handleKeyPress}
tabIndex="0"
>
{props.itemRender(props.page, 'page', <a>{props.page}</a>)}
{itemNode}
</li>
);
};
Expand Down
49 changes: 37 additions & 12 deletions src/Pagination.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class Pagination extends React.Component {
nextIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
jumpPrevIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
jumpNextIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
pagerCount: PropTypes.number,
};

static defaultProps = {
Expand All @@ -74,16 +75,23 @@ class Pagination extends React.Component {
locale: LOCALE,
style: {},
itemRender: defaultItemRender,
pagerCount: 5,
};

constructor(props) {
super(props);

const hasOnChange = props.onChange !== noop;
const hasCurrent = ('current' in props);
const hasShowLessItems = ('showLessItems' in props);
if (hasCurrent && !hasOnChange) {
console.warn('Warning: You provided a `current` prop to a Pagination component without an `onChange` handler. This will render a read-only component.'); // eslint-disable-line
}
if (hasShowLessItems && props.showLessItems) {
console.warn(
'Warning: `showLessItems` is deprecated since 1.18.0. Please use pagerCount instead.'
) // eslint-disable-line
}

let current = props.defaultCurrent;
if ('current' in props) {
Expand Down Expand Up @@ -143,13 +151,17 @@ class Pagination extends React.Component {
}

getJumpPrevPage = () => {
return Math.max(1, this.state.current - (this.props.showLessItems ? 3 : 5));
const { showLessItems } = this.props;
const hasPagerCount = this.hasPagerCount();
return Math.max(1, this.state.current - (showLessItems && !hasPagerCount ? 3 : 5));
}

getJumpNextPage = () => {
const { showLessItems } = this.props;
const hasPagerCount = this.hasPagerCount();
return Math.min(
calculatePage(undefined, this.state, this.props),
this.state.current + (this.props.showLessItems ? 3 : 5)
this.state.current + (showLessItems && !hasPagerCount ? 3 : 5)
);
}

Expand Down Expand Up @@ -316,6 +328,8 @@ class Pagination extends React.Component {
}
}

hasPagerCount = () => !(this.props.pagerCount === 5)

render() {
// When hideOnSinglePage is true and there is only 1 page, hide the pager
if (this.props.hideOnSinglePage === true && this.props.total <= this.state.pageSize) {
Expand All @@ -334,8 +348,17 @@ class Pagination extends React.Component {
let lastPager = null;
let gotoButton = null;

const { pagerCount, showLessItems } = props;
// `pagerCount` priority is greater than `showLessItems`.
const hasPagerCount = this.hasPagerCount();
const boundary = pagerCount === 0 ? 0 : 1;
const pagerCountBoundary = pagerCount % 2 !== 0 ? 0 : boundary;
const boundaryRemainder = hasPagerCount ? pagerCountBoundary : 0;
const halfPagerCount = Math.max(0, Math.floor((pagerCount - 1) / 2));
const halfHasLessItemsCount = showLessItems ? 1 : halfPagerCount;
const pageBufferSize = hasPagerCount ? halfPagerCount : halfHasLessItemsCount;

const goButton = (props.showQuickJumper && props.showQuickJumper.goButton);
const pageBufferSize = props.showLessItems ? 1 : 2;
const { current, pageSize } = this.state;

const prevPage = current - 1 > 0 ? current - 1 : 0;
Expand Down Expand Up @@ -425,7 +448,7 @@ class Pagination extends React.Component {
);
}

if (allPages <= 5 + pageBufferSize * 2) {
if (allPages <= pageBufferSize * 2 + boundaryRemainder + 1) {
const pagerProps = {
locale,
rootPrefixCls: prefixCls,
Expand Down Expand Up @@ -456,8 +479,8 @@ class Pagination extends React.Component {
);
}
} else {
const prevItemTitle = props.showLessItems ? locale.prev_3 : locale.prev_5;
const nextItemTitle = props.showLessItems ? locale.next_3 : locale.next_5;
const prevItemTitle = showLessItems && !hasPagerCount ? locale.prev_3 : locale.prev_5;
const nextItemTitle = showLessItems && !hasPagerCount ? locale.next_3 : locale.next_5;
if (props.showPrevNextJumpers) {
let jumpPrevClassString = `${prefixCls}-jump-prev`;
if (props.jumpPrevIcon) {
Expand Down Expand Up @@ -500,6 +523,7 @@ class Pagination extends React.Component {
</li>
);
}

lastPager = (
<Pager
locale={props.locale}
Expand All @@ -517,6 +541,7 @@ class Pagination extends React.Component {
firstPager = (
<Pager
locale={props.locale}
first
rootPrefixCls={prefixCls}
onClick={this.handleChange}
onKeyPress={this.runIfEnter}
Expand All @@ -528,15 +553,15 @@ class Pagination extends React.Component {
/>
);

let left = Math.max(1, current - pageBufferSize);
let left = Math.max(1, current - pageBufferSize - boundaryRemainder);
let right = Math.min(current + pageBufferSize, allPages);

if (current - 1 <= pageBufferSize) {
right = 1 + pageBufferSize * 2;
right = 1 + pageBufferSize * 2 + boundaryRemainder;
}

if (allPages - current <= pageBufferSize) {
left = allPages - pageBufferSize * 2;
if (allPages - current < pageBufferSize) {
left = allPages - pageBufferSize * 2 - boundaryRemainder;
}

for (let i = left; i <= right; i++) {
Expand All @@ -556,13 +581,13 @@ class Pagination extends React.Component {
);
}

if (current - 1 >= pageBufferSize * 2 && current !== 1 + 2) {
if (current - boundaryRemainder - 2 > pageBufferSize) {
pagerList[0] = React.cloneElement(pagerList[0], {
className: `${prefixCls}-item-after-jump-prev`,
});
pagerList.unshift(jumpPrev);
}
if (allPages - current >= pageBufferSize * 2 && current !== allPages - 2) {
if (allPages - current > pageBufferSize && current !== allPages - pageBufferSize - 1) {
pagerList[pagerList.length - 1] = React.cloneElement(pagerList[pagerList.length - 1], {
className: `${prefixCls}-item-before-jump-next`,
});
Expand Down
139 changes: 139 additions & 0 deletions tests/Pagination.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,142 @@ describe('data and aria props', () => {
});
});
});

describe('pagerCount props', () => {
describe('with pagerCount, when hide first, last', () => {
const container = document.createElement('div');
document.body.appendChild(container);

const itemRender = (current, type, element) => {
if (type === 'jump-first' || type === 'jump-last') {
return null;
}
return element;
};

it('pageCount is 2, total is 10, show 1 pager', (done) => {
ReactDOM.render(
<Pagination total={10} itemRender={itemRender} pagerCount={2} />,
container,
function () {
const pagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(pagers.length).to.be(1);
done();
}
);
});

it('pageCount is 2, total is 11, show 2 pager', (done) => {
ReactDOM.render(
<Pagination total={11} itemRender={itemRender} pagerCount={2} />,
container,
function () {
const pagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(pagers.length).to.be(2);
done();
}
);
});

it('should show 10 pagers if pageCount equals 10', (done) => {
ReactDOM.render(
<Pagination total={101} itemRender={itemRender} pagerCount={10} />,
container,
function () {
const pagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(pagers.length).to.be(10);

const eighthPager = TestUtils.findRenderedDOMComponentWithClass(
this,
'rc-pagination-item-8'
);
expect(TestUtils.isDOMComponent(eighthPager)).to.be(true);
Simulate.click(eighthPager);
setTimeout(() => {
const afterPagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(afterPagers.length).to.be(10);
done();
}, 10);
}
);
});
});

describe('pagerCount is even', () => {
it('pageCount is even', (done) => {
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(
<Pagination total={500} pagerCount={8} />,
container,
function () {
const pagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(pagers.length).to.be(9);

const sixthPager = TestUtils.findRenderedDOMComponentWithClass(
this,
'rc-pagination-item-6'
);
expect(TestUtils.isDOMComponent(sixthPager)).to.be(true);
Simulate.click(sixthPager);
setTimeout(() => {
const afterPagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(afterPagers.length).to.be(10);
done();
}, 10);
}
);
});

it('defaultCurrent is last page', (done) => {
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(
<Pagination total={500} pagerCount={7} defaultCurrent={50} />,
container,
function () {
setTimeout(() => {
const pagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(pagers.length).to.be(8);

const pager46 = TestUtils.findRenderedDOMComponentWithClass(
this,
'rc-pagination-item-46'
);
expect(TestUtils.isDOMComponent(pager46)).to.be(true);
Simulate.click(pager46);
setTimeout(() => {
const afterPagers = TestUtils.scryRenderedDOMComponentsWithClass(
this,
'rc-pagination-item'
);
expect(afterPagers.length).to.be(9);
done();
}, 10);
}, 10);
}
);
});
});
});