fix pagination broken when bootstrap4

This commit is contained in:
AllenFang 2018-07-22 17:32:41 +08:00
parent 495875792f
commit 3f957db56b
7 changed files with 232 additions and 92 deletions

View File

@ -0,0 +1,6 @@
import React from 'react';
// consider to have a common lib?1
export const BootstrapContext = React.createContext({
bootstrap4: false
});

View File

@ -5,6 +5,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Const from './const';
import { BootstrapContext } from './bootstrap';
import Pagination from './pagination';
import { getByCurrPage, alignPage } from './page';
@ -123,7 +124,7 @@ export default (
render() {
let { data } = this.props;
const { pagination: { options } } = this.props;
const { pagination: { options }, bootstrap4 } = this.props;
const { currPage, currSizePerPage } = this;
const withFirstAndLast = typeof options.withFirstAndLast === 'undefined' ?
Const.With_FIRST_AND_LAST : options.withFirstAndLast;
@ -148,31 +149,33 @@ export default (
return (
<PaginationContext.Provider value={ { data } }>
{ this.props.children }
<Pagination
key="pagination"
dataSize={ options.totalSize || this.props.data.length }
currPage={ currPage }
currSizePerPage={ currSizePerPage }
onPageChange={ this.handleChangePage }
onSizePerPageChange={ this.handleChangeSizePerPage }
sizePerPageList={ options.sizePerPageList || Const.SIZE_PER_PAGE_LIST }
paginationSize={ options.paginationSize || Const.PAGINATION_SIZE }
pageStartIndex={ pageStartIndex }
withFirstAndLast={ withFirstAndLast }
alwaysShowAllBtns={ alwaysShowAllBtns }
hideSizePerPage={ hideSizePerPage }
hidePageListOnlyOnePage={ hidePageListOnlyOnePage }
showTotal={ options.showTotal }
paginationTotalRenderer={ options.paginationTotalRenderer }
firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT }
prePageText={ options.prePageText || Const.PRE_PAGE_TEXT }
nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT }
lastPageText={ options.lastPageText || Const.LAST_PAGE_TEXT }
prePageTitle={ options.prePageTitle || Const.PRE_PAGE_TITLE }
nextPageTitle={ options.nextPageTitle || Const.NEXT_PAGE_TITLE }
firstPageTitle={ options.firstPageTitle || Const.FIRST_PAGE_TITLE }
lastPageTitle={ options.lastPageTitle || Const.LAST_PAGE_TITLE }
/>
<BootstrapContext.Provider value={ { bootstrap4 } }>
<Pagination
key="pagination"
dataSize={ options.totalSize || this.props.data.length }
currPage={ currPage }
currSizePerPage={ currSizePerPage }
onPageChange={ this.handleChangePage }
onSizePerPageChange={ this.handleChangeSizePerPage }
sizePerPageList={ options.sizePerPageList || Const.SIZE_PER_PAGE_LIST }
paginationSize={ options.paginationSize || Const.PAGINATION_SIZE }
pageStartIndex={ pageStartIndex }
withFirstAndLast={ withFirstAndLast }
alwaysShowAllBtns={ alwaysShowAllBtns }
hideSizePerPage={ hideSizePerPage }
hidePageListOnlyOnePage={ hidePageListOnlyOnePage }
showTotal={ options.showTotal }
paginationTotalRenderer={ options.paginationTotalRenderer }
firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT }
prePageText={ options.prePageText || Const.PRE_PAGE_TEXT }
nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT }
lastPageText={ options.lastPageText || Const.LAST_PAGE_TEXT }
prePageTitle={ options.prePageTitle || Const.PRE_PAGE_TITLE }
nextPageTitle={ options.nextPageTitle || Const.NEXT_PAGE_TITLE }
firstPageTitle={ options.firstPageTitle || Const.FIRST_PAGE_TITLE }
lastPageTitle={ options.lastPageTitle || Const.LAST_PAGE_TITLE }
/>
</BootstrapContext.Provider>
</PaginationContext.Provider>
);
}

View File

@ -1,6 +1,7 @@
import React from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
import { BootstrapContext } from './bootstrap';
import SizePerPageOption from './size-per-page-option';
const sizePerPageDefaultClass = 'react-bs-table-sizePerPage-dropdown';
@ -20,44 +21,60 @@ const SizePerPageDropDown = (props) => {
} = props;
const dropDownStyle = { visibility: hidden ? 'hidden' : 'visible' };
const openClass = open ? 'open show' : '';
const dropdownClasses = cs(
open ? 'open show' : '',
openClass,
sizePerPageDefaultClass,
variation,
className,
);
return (
<span
style={ dropDownStyle }
className={ dropdownClasses }
>
<button
id="pageDropDown"
className={ `btn ${btnContextual} dropdown-toggle` }
data-toggle="dropdown"
aria-expanded={ open }
onClick={ onClick }
onBlur={ onBlur }
>
{ currSizePerPage }
<span>
{ ' ' }
<span className="caret" />
</span>
</button>
<ul className="dropdown-menu" role="menu" aria-labelledby="pageDropDown">
{
options.map(option => (
<SizePerPageOption
{ ...option }
key={ option.text }
onSizePerPageChange={ onSizePerPageChange }
/>
))
}
</ul>
</span>
<BootstrapContext.Consumer>
{
({ bootstrap4 }) => (
<span
style={ dropDownStyle }
className={ dropdownClasses }
>
<button
id="pageDropDown"
className={ `btn ${btnContextual} dropdown-toggle` }
data-toggle="dropdown"
aria-expanded={ open }
onClick={ onClick }
onBlur={ onBlur }
>
{ currSizePerPage }
{ ' ' }
{
bootstrap4 ? null : (
<span>
<span className="caret" />
</span>
)
}
</button>
<ul
className={ `dropdown-menu ${openClass}` }
role="menu"
aria-labelledby="pageDropDown"
>
{
options.map(option => (
<SizePerPageOption
{ ...option }
key={ option.text }
bootstrap4={ bootstrap4 }
onSizePerPageChange={ onSizePerPageChange }
/>
))
}
</ul>
</span>
)
}
</BootstrapContext.Consumer>
);
};

View File

@ -5,9 +5,28 @@ import PropTypes from 'prop-types';
const SizePerPageOption = ({
text,
page,
onSizePerPageChange
}) => (
<li key={ text } role="presentation" className="dropdown-item">
onSizePerPageChange,
bootstrap4
}) => (bootstrap4 ? (
<a
href="#"
tabIndex="-1"
role="menuitem"
className="dropdown-item"
data-page={ page }
onMouseDown={ (e) => {
e.preventDefault();
onSizePerPageChange(page);
} }
>
{ text }
</a>
) : (
<li
key={ text }
role="presentation"
className="dropdown-item"
>
<a
href="#"
tabIndex="-1"
@ -21,12 +40,17 @@ const SizePerPageOption = ({
{ text }
</a>
</li>
);
));
SizePerPageOption.propTypes = {
text: PropTypes.string.isRequired,
page: PropTypes.number.isRequired,
onSizePerPageChange: PropTypes.func.isRequired
onSizePerPageChange: PropTypes.func.isRequired,
bootstrap4: PropTypes.bool
};
SizePerPageOption.defaultProps = {
bootstrap4: false
};
export default SizePerPageOption;

View File

@ -5,6 +5,12 @@ import { shallow } from 'enzyme';
import SizePerPageOption from '../src/size-per-page-option';
import SizePerPageDropDown from '../src/size-per-page-dropdown';
const shallowWithContext = (elem, context = {}) => {
const wrapper = shallow(elem);
const Children = wrapper.props().children(context);
return shallow(Children);
};
describe('SizePerPageDropDown', () => {
let wrapper;
const currSizePerPage = '25';
@ -28,8 +34,9 @@ describe('SizePerPageDropDown', () => {
describe('default SizePerPageDropDown component', () => {
beforeEach(() => {
wrapper = shallow(
<SizePerPageDropDown { ...props } />
wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } />,
{ bootstrap4: false }
);
});
@ -47,6 +54,7 @@ describe('SizePerPageDropDown', () => {
const option = options[i];
expect(sizePerPage.prop('text')).toEqual(option.text);
expect(sizePerPage.prop('page')).toEqual(option.page);
expect(sizePerPage.prop('bootstrap4')).toBeFalsy();
expect(sizePerPage.prop('onSizePerPageChange')).toEqual(onSizePerPageChange);
});
});
@ -61,10 +69,52 @@ describe('SizePerPageDropDown', () => {
});
});
describe('when bootstrap4 context is true', () => {
beforeEach(() => {
wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } />,
{ bootstrap4: true }
);
});
it('should rendering SizePerPageDropDown correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('button').length).toBe(1);
expect(wrapper.find('button').text()).toEqual(`${currSizePerPage} `);
});
it('should rendering SizePerPageOption successfully', () => {
expect(wrapper.find('ul.dropdown-menu').length).toBe(1);
const sizePerPageOptions = wrapper.find(SizePerPageOption);
expect(sizePerPageOptions.length).toBe(options.length);
sizePerPageOptions.forEach((sizePerPage, i) => {
const option = options[i];
expect(sizePerPage.prop('text')).toEqual(option.text);
expect(sizePerPage.prop('page')).toEqual(option.page);
expect(sizePerPage.prop('bootstrap4')).toBeTruthy();
expect(sizePerPage.prop('onSizePerPageChange')).toEqual(onSizePerPageChange);
});
});
it('no need to render caret', () => {
expect(wrapper.find('.caret')).toHaveLength(0);
});
it('default variation is dropdown', () => {
expect(wrapper.hasClass('dropdown')).toBeTruthy();
});
it('default dropdown is not open', () => {
expect(wrapper.hasClass('open show')).toBeFalsy();
expect(wrapper.find('[aria-expanded=false]').length).toBe(1);
});
});
describe('when open prop is true', () => {
beforeEach(() => {
wrapper = shallow(
<SizePerPageDropDown { ...props } open />
wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } open />,
{ bootstrap4: false }
);
});
@ -76,8 +126,9 @@ describe('SizePerPageDropDown', () => {
describe('when hidden prop is true', () => {
beforeEach(() => {
wrapper = shallow(
<SizePerPageDropDown { ...props } hidden />
wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } hidden />,
{ bootstrap4: false }
);
});
@ -89,8 +140,9 @@ describe('SizePerPageDropDown', () => {
describe('when btnContextual prop is defined', () => {
const contextual = 'btn-warning';
beforeEach(() => {
wrapper = shallow(
<SizePerPageDropDown { ...props } btnContextual={ contextual } />
wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } btnContextual={ contextual } />,
{ bootstrap4: false }
);
});
@ -102,8 +154,9 @@ describe('SizePerPageDropDown', () => {
describe('when variation prop is defined', () => {
const variation = 'dropup';
beforeEach(() => {
wrapper = shallow(
<SizePerPageDropDown { ...props } variation={ variation } />
wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } variation={ variation } />,
{ bootstrap4: false }
);
});
@ -115,8 +168,9 @@ describe('SizePerPageDropDown', () => {
describe('when className prop is defined', () => {
const className = 'custom-class';
beforeEach(() => {
wrapper = shallow(
<SizePerPageDropDown { ...props } className={ className } />
wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } className={ className } />,
{ bootstrap4: false }
);
});

View File

@ -11,29 +11,64 @@ describe('SizePerPageOption', () => {
const onSizePerPageChange = sinon.stub();
beforeEach(() => {
const props = { text, page, onSizePerPageChange };
wrapper = shallow(
<SizePerPageOption { ...props } />
);
onSizePerPageChange.reset();
});
it('should render SizePerPageOption correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('.dropdown-item').length).toBe(1);
expect(wrapper.find(`[data-page=${page}]`).length).toBe(1);
expect(wrapper.text()).toEqual(text);
});
describe('when MouseDown event happen', () => {
const preventDefault = sinon.stub();
describe('when bootstrap4 prop is true', () => {
beforeEach(() => {
wrapper.find('a').simulate('mousedown', { preventDefault });
const props = { text, page, onSizePerPageChange };
wrapper = shallow(
<SizePerPageOption { ...props } />
);
});
it('should calling props.onSizePerPageChange correctly', () => {
expect(preventDefault.calledOnce).toBeTruthy();
expect(onSizePerPageChange.calledOnce).toBeTruthy();
expect(onSizePerPageChange.calledWith(page)).toBeTruthy();
it('should render SizePerPageOption correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('li.dropdown-item').length).toBe(1);
expect(wrapper.find(`[data-page=${page}]`).length).toBe(1);
expect(wrapper.text()).toEqual(text);
});
describe('when MouseDown event happen', () => {
const preventDefault = sinon.stub();
beforeEach(() => {
wrapper.find('a').simulate('mousedown', { preventDefault });
});
it('should calling props.onSizePerPageChange correctly', () => {
expect(preventDefault.calledOnce).toBeTruthy();
expect(onSizePerPageChange.calledOnce).toBeTruthy();
expect(onSizePerPageChange.calledWith(page)).toBeTruthy();
});
});
});
describe('when bootstrap4 prop is true', () => {
beforeEach(() => {
const props = { text, page, onSizePerPageChange };
wrapper = shallow(
<SizePerPageOption { ...props } bootstrap4 />
);
});
it('should render SizePerPageOption correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('a.dropdown-item').length).toBe(1);
expect(wrapper.find(`[data-page=${page}]`).length).toBe(1);
expect(wrapper.text()).toEqual(text);
});
describe('when MouseDown event happen', () => {
const preventDefault = sinon.stub();
beforeEach(() => {
wrapper.find('a').simulate('mousedown', { preventDefault });
});
it('should calling props.onSizePerPageChange correctly', () => {
expect(preventDefault.calledOnce).toBeTruthy();
expect(onSizePerPageChange.calledOnce).toBeTruthy();
expect(onSizePerPageChange.calledWith(page)).toBeTruthy();
});
});
});
});

View File

@ -155,6 +155,7 @@ const withContext = Base =>
ref={ n => this.paginationContext = n }
pagination={ this.props.pagination }
data={ rootProps.getData(filterProps, searchProps, sortProps) }
bootstrap4={ this.props.bootstrap4 }
>
<this.PaginationContext.Consumer>
{