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

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import cs from 'classnames'; import cs from 'classnames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { BootstrapContext } from './bootstrap';
import SizePerPageOption from './size-per-page-option'; import SizePerPageOption from './size-per-page-option';
const sizePerPageDefaultClass = 'react-bs-table-sizePerPage-dropdown'; const sizePerPageDefaultClass = 'react-bs-table-sizePerPage-dropdown';
@ -20,44 +21,60 @@ const SizePerPageDropDown = (props) => {
} = props; } = props;
const dropDownStyle = { visibility: hidden ? 'hidden' : 'visible' }; const dropDownStyle = { visibility: hidden ? 'hidden' : 'visible' };
const openClass = open ? 'open show' : '';
const dropdownClasses = cs( const dropdownClasses = cs(
open ? 'open show' : '', openClass,
sizePerPageDefaultClass, sizePerPageDefaultClass,
variation, variation,
className, className,
); );
return ( return (
<span <BootstrapContext.Consumer>
style={ dropDownStyle } {
className={ dropdownClasses } ({ bootstrap4 }) => (
> <span
<button style={ dropDownStyle }
id="pageDropDown" className={ dropdownClasses }
className={ `btn ${btnContextual} dropdown-toggle` } >
data-toggle="dropdown" <button
aria-expanded={ open } id="pageDropDown"
onClick={ onClick } className={ `btn ${btnContextual} dropdown-toggle` }
onBlur={ onBlur } data-toggle="dropdown"
> aria-expanded={ open }
{ currSizePerPage } onClick={ onClick }
<span> onBlur={ onBlur }
{ ' ' } >
<span className="caret" /> { currSizePerPage }
</span> { ' ' }
</button> {
<ul className="dropdown-menu" role="menu" aria-labelledby="pageDropDown"> bootstrap4 ? null : (
{ <span>
options.map(option => ( <span className="caret" />
<SizePerPageOption </span>
{ ...option } )
key={ option.text } }
onSizePerPageChange={ onSizePerPageChange } </button>
/> <ul
)) className={ `dropdown-menu ${openClass}` }
} role="menu"
</ul> aria-labelledby="pageDropDown"
</span> >
{
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 = ({ const SizePerPageOption = ({
text, text,
page, page,
onSizePerPageChange onSizePerPageChange,
}) => ( bootstrap4
<li key={ text } role="presentation" className="dropdown-item"> }) => (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 <a
href="#" href="#"
tabIndex="-1" tabIndex="-1"
@ -21,12 +40,17 @@ const SizePerPageOption = ({
{ text } { text }
</a> </a>
</li> </li>
); ));
SizePerPageOption.propTypes = { SizePerPageOption.propTypes = {
text: PropTypes.string.isRequired, text: PropTypes.string.isRequired,
page: PropTypes.number.isRequired, page: PropTypes.number.isRequired,
onSizePerPageChange: PropTypes.func.isRequired onSizePerPageChange: PropTypes.func.isRequired,
bootstrap4: PropTypes.bool
};
SizePerPageOption.defaultProps = {
bootstrap4: false
}; };
export default SizePerPageOption; export default SizePerPageOption;

View File

@ -5,6 +5,12 @@ import { shallow } from 'enzyme';
import SizePerPageOption from '../src/size-per-page-option'; import SizePerPageOption from '../src/size-per-page-option';
import SizePerPageDropDown from '../src/size-per-page-dropdown'; 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', () => { describe('SizePerPageDropDown', () => {
let wrapper; let wrapper;
const currSizePerPage = '25'; const currSizePerPage = '25';
@ -28,8 +34,9 @@ describe('SizePerPageDropDown', () => {
describe('default SizePerPageDropDown component', () => { describe('default SizePerPageDropDown component', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } /> <SizePerPageDropDown { ...props } />,
{ bootstrap4: false }
); );
}); });
@ -47,6 +54,7 @@ describe('SizePerPageDropDown', () => {
const option = options[i]; const option = options[i];
expect(sizePerPage.prop('text')).toEqual(option.text); expect(sizePerPage.prop('text')).toEqual(option.text);
expect(sizePerPage.prop('page')).toEqual(option.page); expect(sizePerPage.prop('page')).toEqual(option.page);
expect(sizePerPage.prop('bootstrap4')).toBeFalsy();
expect(sizePerPage.prop('onSizePerPageChange')).toEqual(onSizePerPageChange); 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', () => { describe('when open prop is true', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } open /> <SizePerPageDropDown { ...props } open />,
{ bootstrap4: false }
); );
}); });
@ -76,8 +126,9 @@ describe('SizePerPageDropDown', () => {
describe('when hidden prop is true', () => { describe('when hidden prop is true', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } hidden /> <SizePerPageDropDown { ...props } hidden />,
{ bootstrap4: false }
); );
}); });
@ -89,8 +140,9 @@ describe('SizePerPageDropDown', () => {
describe('when btnContextual prop is defined', () => { describe('when btnContextual prop is defined', () => {
const contextual = 'btn-warning'; const contextual = 'btn-warning';
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } btnContextual={ contextual } /> <SizePerPageDropDown { ...props } btnContextual={ contextual } />,
{ bootstrap4: false }
); );
}); });
@ -102,8 +154,9 @@ describe('SizePerPageDropDown', () => {
describe('when variation prop is defined', () => { describe('when variation prop is defined', () => {
const variation = 'dropup'; const variation = 'dropup';
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } variation={ variation } /> <SizePerPageDropDown { ...props } variation={ variation } />,
{ bootstrap4: false }
); );
}); });
@ -115,8 +168,9 @@ describe('SizePerPageDropDown', () => {
describe('when className prop is defined', () => { describe('when className prop is defined', () => {
const className = 'custom-class'; const className = 'custom-class';
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = shallowWithContext(
<SizePerPageDropDown { ...props } className={ className } /> <SizePerPageDropDown { ...props } className={ className } />,
{ bootstrap4: false }
); );
}); });

View File

@ -11,29 +11,64 @@ describe('SizePerPageOption', () => {
const onSizePerPageChange = sinon.stub(); const onSizePerPageChange = sinon.stub();
beforeEach(() => { beforeEach(() => {
const props = { text, page, onSizePerPageChange }; onSizePerPageChange.reset();
wrapper = shallow(
<SizePerPageOption { ...props } />
);
}); });
it('should render SizePerPageOption correctly', () => { describe('when bootstrap4 prop is true', () => {
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();
beforeEach(() => { beforeEach(() => {
wrapper.find('a').simulate('mousedown', { preventDefault }); const props = { text, page, onSizePerPageChange };
wrapper = shallow(
<SizePerPageOption { ...props } />
);
}); });
it('should calling props.onSizePerPageChange correctly', () => { it('should render SizePerPageOption correctly', () => {
expect(preventDefault.calledOnce).toBeTruthy(); expect(wrapper.length).toBe(1);
expect(onSizePerPageChange.calledOnce).toBeTruthy(); expect(wrapper.find('li.dropdown-item').length).toBe(1);
expect(onSizePerPageChange.calledWith(page)).toBeTruthy(); 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 } ref={ n => this.paginationContext = n }
pagination={ this.props.pagination } pagination={ this.props.pagination }
data={ rootProps.getData(filterProps, searchProps, sortProps) } data={ rootProps.getData(filterProps, searchProps, sortProps) }
bootstrap4={ this.props.bootstrap4 }
> >
<this.PaginationContext.Consumer> <this.PaginationContext.Consumer>
{ {