diff --git a/docs/cell-edit.md b/docs/cell-edit.md index cc56f6c..e1b4ab5 100644 --- a/docs/cell-edit.md +++ b/docs/cell-edit.md @@ -69,7 +69,7 @@ const cellEdit: { // omit... beforeSaveCell(oldValue, newValue, row, column, done) { setTimeout(() => { - if (confirm('Do you want to accep this change?')) { + if (confirm('Do you want to accept this change?')) { done(); // contine to save the changes } else { done(false); // reject the changes diff --git a/packages/react-bootstrap-table2-example/examples/pagination/custom-page-list-with-search.js b/packages/react-bootstrap-table2-example/examples/pagination/custom-page-list-with-search.js index 87d5d74..257a25b 100644 --- a/packages/react-bootstrap-table2-example/examples/pagination/custom-page-list-with-search.js +++ b/packages/react-bootstrap-table2-example/examples/pagination/custom-page-list-with-search.js @@ -7,7 +7,7 @@ import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit'; import Code from 'components/common/code-block'; import { productsGenerator } from 'utils/common'; -const products = productsGenerator(21); +const products = productsGenerator(40); const { SearchBar } = Search; const columns = [{ @@ -24,61 +24,12 @@ import paginationFactory, { PaginationProvider, PaginationListStandalone } from import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; class Table extends React.Component { - render() { - const options = { - custom: true, - paginationSize: 4, - pageStartIndex: 1, - firstPageText: 'First', - prePageText: 'Back', - nextPageText: 'Next', - lastPageText: 'Last', - nextPageTitle: 'First page', - prePageTitle: 'Pre page', - firstPageTitle: 'Next page', - lastPageTitle: 'Last page', - showTotal: true, - totalSize: products.length - }; - const contentTable = ({ paginationProps, paginationTableProps }) => ( -
- -
-
- -
-
- -
- ); + state = { products } - return ( -
-

PaginationProvider will care the data size change. You dont do anything

- - { contentTable } - - { sourceCode } -
- ); + loadData = () => { + this.setState({ products: productsGenerator(17) }); } -} -`; -export default class Table extends React.Component { render() { const options = { custom: true, @@ -93,15 +44,84 @@ export default class Table extends React.Component { firstPageTitle: 'Next page', lastPageTitle: 'Last page', showTotal: true, - totalSize: products.length + totalSize: this.state.products.length }; const contentTable = ({ paginationProps, paginationTableProps }) => (
+ + { + toolkitprops => ( +
+ + +
+ ) + } +
+ +
+ ); + + return ( +
+

PaginationProvider will care the data size change. You dont do anything

+ + { contentTable } + + { sourceCode } +
+ ); + } +} +`; + +export default class Table extends React.Component { + state = { products } + + loadData = () => { + this.setState({ products: productsGenerator(17) }); + } + + render() { + const options = { + custom: true, + paginationSize: 4, + pageStartIndex: 1, + firstPageText: 'First', + prePageText: 'Back', + nextPageText: 'Next', + lastPageText: 'Last', + nextPageTitle: 'First page', + prePageTitle: 'Pre page', + firstPageTitle: 'Next page', + lastPageTitle: 'Last page', + showTotal: true, + totalSize: this.state.products.length + }; + const contentTable = ({ paginationProps, paginationTableProps }) => ( +
+ + + { diff --git a/packages/react-bootstrap-table2-example/examples/pagination/custome-page-list-with-filter.js b/packages/react-bootstrap-table2-example/examples/pagination/custome-page-list-with-filter.js index eefd780..1997d37 100644 --- a/packages/react-bootstrap-table2-example/examples/pagination/custome-page-list-with-filter.js +++ b/packages/react-bootstrap-table2-example/examples/pagination/custome-page-list-with-filter.js @@ -3,28 +3,70 @@ import React from 'react'; import BootstrapTable from 'react-bootstrap-table-next'; import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator'; -import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; +import filterFactory, { textFilter, selectFilter } from 'react-bootstrap-table2-filter'; import Code from 'components/common/code-block'; -import { productsGenerator } from 'utils/common'; +import { productsQualityGenerator } from 'utils/common'; -const products = productsGenerator(21); +const products = productsQualityGenerator(21); + +const selectOptions = { + 0: 'good', + 1: 'Bad', + 2: 'unknown' +}; const columns = [{ dataField: 'id', - text: 'Product ID', - filter: textFilter({}) + text: 'Product ID' }, { dataField: 'name', text: 'Product Name', filter: textFilter() +}, { + dataField: 'quality', + text: 'Product Quailty', + formatter: cell => selectOptions[cell], + filter: selectFilter({ + options: selectOptions, + defaultValue: 0 + }) }]; const sourceCode = `\ import BootstrapTable from 'react-bootstrap-table-next'; import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator'; -import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; +import filterFactory, { textFilter, selectFilter } from 'react-bootstrap-table2-filter'; + +const selectOptions = { + 0: 'good', + 1: 'Bad', + 2: 'unknown' +}; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + filter: textFilter() +}, { + dataField: 'quality', + text: 'Product Quailty', + formatter: cell => selectOptions[cell], + filter: selectFilter({ + options: selectOptions, + defaultValue: 0 + }) +}]; class Table extends React.Component { + state = { products } + + loadData = () => { + this.setState({ products: productsQualityGenerator(40, 7) }); + } + render() { const options = { custom: true, @@ -39,10 +81,11 @@ class Table extends React.Component { firstPageTitle: 'Next page', lastPageTitle: 'Last page', showTotal: true, - totalSize: products.length + totalSize: this.state.products.length }; const contentTable = ({ paginationProps, paginationTableProps }) => (
+
@@ -50,10 +93,9 @@ class Table extends React.Component { striped hover keyField="id" - data={ products } + data={ this.state.products } columns={ columns } filter={ filterFactory() } - cellEdit={ cellEditFactory() } { ...paginationTableProps } />
@@ -72,7 +114,6 @@ class Table extends React.Component { > { contentTable } - { sourceCode }
); } @@ -80,6 +121,12 @@ class Table extends React.Component { `; export default class Table extends React.Component { + state = { products } + + loadData = () => { + this.setState({ products: productsQualityGenerator(40, 7) }); + } + render() { const options = { custom: true, @@ -94,10 +141,11 @@ export default class Table extends React.Component { firstPageTitle: 'Next page', lastPageTitle: 'Last page', showTotal: true, - totalSize: products.length + totalSize: this.state.products.length }; const contentTable = ({ paginationProps, paginationTableProps }) => (
+
@@ -105,7 +153,7 @@ export default class Table extends React.Component { striped hover keyField="id" - data={ products } + data={ this.state.products } columns={ columns } filter={ filterFactory() } { ...paginationTableProps } diff --git a/packages/react-bootstrap-table2-example/examples/pagination/pagination-with-dynamic-data.js b/packages/react-bootstrap-table2-example/examples/pagination/pagination-with-dynamic-data.js new file mode 100644 index 0000000..cdd6f3d --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/pagination/pagination-with-dynamic-data.js @@ -0,0 +1,150 @@ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import Code from 'components/common/code-block'; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import paginationFactory from 'react-bootstrap-table2-paginator'; + +class BookList extends React.Component { + state = { + books: [ + { id: '1', name: 'Book 1' }, + { id: '2', name: 'Book 2' }, + { id: '3', name: 'Book 3' }, + { id: '4', name: 'Book 4' }, + { id: '5', name: 'Book 5' }, + { id: '6', name: 'Book 6' } + ] + }; + + deleteBookWithId = () => { + const lastOneId = this.state.books.length; + const updatedBooks = this.state.books.filter(m => m.id !== lastOneId.toString()); + this.setState({ books: updatedBooks }); + }; + + addBook = () => { + const lastOneId = this.state.books.length + 1; + this.setState({ books: [...this.state.books, { + id: \`$\{lastOneId}\`, name: \`Book $\{lastOneId}\` + }] }); + } + + render() { + const options = { + // pageStartIndex: 0, + sizePerPage: 5, + hideSizePerPage: true, + hidePageListOnlyOnePage: true + }; + const columns = [ + { + dataField: 'id', + text: 'Product ID', + Cell: row => ( +
+ { row.value } +
+ ) + }, + { + dataField: 'name', + text: 'Product Name' + } + ]; + + return ( + + + + + { sourceCode } + + ); + } +`; + +export default class BookList extends React.Component { + state = { + books: [ + { id: '1', name: 'Book 1' }, + { id: '2', name: 'Book 2' }, + { id: '3', name: 'Book 3' }, + { id: '4', name: 'Book 4' }, + { id: '5', name: 'Book 5' }, + { id: '6', name: 'Book 6' }, + { id: '7', name: 'Book 6' }, + { id: '8', name: 'Book 6' }, + { id: '9', name: 'Book 6' }, + { id: '10', name: 'Book 6' }, + { id: '11', name: 'Book 6' } + ] + }; + + deleteBookWithId = () => { + const lastOneId = this.state.books.length; + const updatedBooks = this.state.books.filter(m => m.id !== lastOneId.toString()); + this.setState({ books: updatedBooks }); + }; + + addBook = () => { + const lastOneId = this.state.books.length + 1; + this.setState({ books: [...this.state.books, { + id: `${lastOneId}`, name: `Book ${lastOneId}` + }] }); + } + + render() { + const options = { + // pageStartIndex: 0, + sizePerPage: 5, + hideSizePerPage: true, + hidePageListOnlyOnePage: true + }; + const columns = [ + { + dataField: 'id', + text: 'Product ID', + Cell: row => ( +
+ { row.value } +
+ ) + }, + { + dataField: 'name', + text: 'Product Name' + } + ]; + + return ( + + + + + { sourceCode } + + ); + } +} diff --git a/packages/react-bootstrap-table2-example/examples/pagination/standalone-size-per-page.js b/packages/react-bootstrap-table2-example/examples/pagination/standalone-size-per-page.js index e49b4f1..40526a2 100644 --- a/packages/react-bootstrap-table2-example/examples/pagination/standalone-size-per-page.js +++ b/packages/react-bootstrap-table2-example/examples/pagination/standalone-size-per-page.js @@ -84,6 +84,7 @@ export default class StandaloneSizePerPage extends React.Component {
rows.map((row) => { return row; }); -export const productsQualityGenerator = (quantity = 5) => +export const productsQualityGenerator = (quantity = 5, factor = 0) => Array.from({ length: quantity }, (value, index) => ({ - id: index, - name: `Item name ${index}`, + id: index + factor, + name: `Item name ${index + factor}`, quality: index % 3 })); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 39be4b7..cc97704 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -163,6 +163,7 @@ import ExpandHooks from 'examples/row-expand/expand-hooks'; // pagination import PaginationTable from 'examples/pagination'; import PaginationHooksTable from 'examples/pagination/pagination-hooks'; +import PaginationWithDynamicData from 'examples/pagination/pagination-with-dynamic-data'; import CustomPaginationTable from 'examples/pagination/custom-pagination'; import CustomPageButtonTable from 'examples/pagination/custom-page-button'; import CustomSizePerPageOptionTable from 'examples/pagination/custom-size-per-page-option'; @@ -404,6 +405,7 @@ storiesOf('Pagination', module) .addDecorator(bootstrapStyle()) .add('Basic Pagination Table', () => ) .add('Pagination Hooks', () => ) + .add('Pagination with Dynamic Data', () => ) .add('Custom Pagination', () => ) .add('Custom Page Button', () => ) .add('Custom Page List', () => ) diff --git a/packages/react-bootstrap-table2-filter/src/context.js b/packages/react-bootstrap-table2-filter/src/context.js index f54e327..e35711f 100644 --- a/packages/react-bootstrap-table2-filter/src/context.js +++ b/packages/react-bootstrap-table2-filter/src/context.js @@ -27,9 +27,8 @@ export default ( this.onFilter = this.onFilter.bind(this); this.doFilter = this.doFilter.bind(this); this.onExternalFilter = this.onExternalFilter.bind(this); - this.state = { - data: props.data - }; + this.data = props.data; + this.isEmitDataChange = false; } componentDidMount() { @@ -39,13 +38,10 @@ export default ( } componentWillReceiveProps(nextProps) { - let nextData = nextProps.data; - if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.state.data)) { - nextData = this.doFilter(nextProps); + // let nextData = nextProps.data; + if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.data)) { + this.doFilter(nextProps, undefined, this.isEmitDataChange); } - this.setState({ - data: nextData - }); } onFilter(column, filterType, initialize = false) { @@ -83,9 +79,7 @@ export default ( if (filter.props.onFilter) { result = filter.props.onFilter(filterVal); } - - result = this.doFilter(this.props, result); - this.setState({ data: result }); + this.doFilter(this.props, result); }; } @@ -95,21 +89,25 @@ export default ( }; } - doFilter(props, customResult) { + doFilter(props, customResult, ignoreEmitDataChange = false) { let result = customResult; const { dataChangeListener, data, columns } = props; result = result || filters(data, columns, _)(this.currFilters); - if (dataChangeListener) { + this.data = result; + if (dataChangeListener && !ignoreEmitDataChange) { + this.isEmitDataChange = true; dataChangeListener.emit('filterChanged', result.length); + } else { + this.isEmitDataChange = false; + this.forceUpdate(); } - return result; } render() { return ( { expect(onFilter).toHaveBeenCalledWith(filterVal); }); - it('should set state.data correctly', () => { + it('should set data correctly', () => { instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal); - expect(instance.state.data).toEqual(mockReturn); + expect(instance.data).toEqual(mockReturn); }); }); diff --git a/packages/react-bootstrap-table2-paginator/README.md b/packages/react-bootstrap-table2-paginator/README.md index ede9040..e5dc52b 100644 --- a/packages/react-bootstrap-table2-paginator/README.md +++ b/packages/react-bootstrap-table2-paginator/README.md @@ -190,6 +190,19 @@ import paginationFactory, { That's it!! The benifit for using standalone is you can much easier to render the standalone component in any posistion. In the future, we will implement more featue like applying `style`, `className` etc on standalone components. +##### Customizable props for `PaginationListStandalone` +* N/A + +##### Customizable props for `SizePerPageDropdownStandalone` +* `open`: `true` to make dropdown show. +* `hidden`: `true` to hide the size per page dropdown. +* `btnContextual`: Set the button contextual +* `variation`: Variation for dropdown, available value is `dropdown` and `dropup`. +* `className`: Custom the class on size per page dropdown + +##### Customizable props for `SizePerPageDropdownStandalone` +* N/A + #### 4.2 Customization Everything If you choose to custom the pagination component by yourself, the `paginationProps` will be important for you. Becasue you have to know for example how to change page or what's the current page etc. Hence, following is all the props in `paginationProps` object: diff --git a/packages/react-bootstrap-table2-paginator/src/data-context.js b/packages/react-bootstrap-table2-paginator/src/data-context.js index d9e9e01..e33f45f 100644 --- a/packages/react-bootstrap-table2-paginator/src/data-context.js +++ b/packages/react-bootstrap-table2-paginator/src/data-context.js @@ -32,7 +32,12 @@ class PaginationDataProvider extends Provider { // user should align the page when the page is not fit to the data size when remote enable if (!this.isRemotePagination() && !custom) { const newPage = alignPage( - nextProps.data.length, this.currPage, currSizePerPage, pageStartIndex); + nextProps.data.length, + this.props.data.length, + this.currPage, + currSizePerPage, + pageStartIndex + ); if (this.currPage !== newPage) { if (onPageChange) { diff --git a/packages/react-bootstrap-table2-paginator/src/page.js b/packages/react-bootstrap-table2-paginator/src/page.js index 1e3e82b..5b63463 100644 --- a/packages/react-bootstrap-table2-paginator/src/page.js +++ b/packages/react-bootstrap-table2-paginator/src/page.js @@ -1,3 +1,5 @@ +import Const from './const'; + const getNormalizedPage = ( page, pageStartIndex @@ -19,12 +21,20 @@ const startIndex = ( export const alignPage = ( dataSize, + prevDataSize, page, sizePerPage, pageStartIndex ) => { - if (page < pageStartIndex || page > (Math.floor(dataSize / sizePerPage) + pageStartIndex)) { - return pageStartIndex; + if (prevDataSize < dataSize) return page; + if (page < pageStartIndex) return pageStartIndex; + if (dataSize <= 0) return pageStartIndex; + if ((page >= (Math.floor(dataSize / sizePerPage) + pageStartIndex)) && pageStartIndex === 1) { + return Math.ceil(dataSize / sizePerPage); + } + if (page >= Math.floor(dataSize / sizePerPage) && pageStartIndex === 0) { + const newPage = Math.ceil(dataSize / sizePerPage); + return newPage - Math.abs((Const.PAGE_START_INDEX - pageStartIndex)); } return page; }; diff --git a/packages/react-bootstrap-table2-paginator/src/size-per-page-dropdown-adapter.js b/packages/react-bootstrap-table2-paginator/src/size-per-page-dropdown-adapter.js index adebceb..b2d6a1f 100644 --- a/packages/react-bootstrap-table2-paginator/src/size-per-page-dropdown-adapter.js +++ b/packages/react-bootstrap-table2-paginator/src/size-per-page-dropdown-adapter.js @@ -48,6 +48,7 @@ const sizePerPageDropdownAdapter = WrappedComponent => } return ( { }); describe('alignPage', () => { - const pageStartIndex = 1; - const sizePerPage = 10; - const page = 3; - describe('if the page does not fit the pages which calculated from the length of data', () => { - it('should return pageStartIndex argument', () => { - expect(alignPage(15, page, sizePerPage, pageStartIndex)).toEqual(pageStartIndex); + let newDataSize; + let prevDataSize; + let currPage; + let pageStartIndex; + let sizePerPage; + + describe('if prevDataSize < newDataSize', () => { + beforeEach(() => { + newDataSize = 10; + prevDataSize = 6; + currPage = 2; + pageStartIndex = 1; + sizePerPage = 5; + }); + it('should return same page', () => { + expect(alignPage( + newDataSize, + prevDataSize, + currPage, + sizePerPage, + pageStartIndex + )).toEqual(currPage); }); }); - describe('if the length of store.data is large than the end page index', () => { - it('should return current page', () => { - expect(alignPage(30, page, sizePerPage, pageStartIndex)).toEqual(page); + describe('if currPage < newDataSize', () => { + beforeEach(() => { + newDataSize = 10; + prevDataSize = 12; + currPage = 0; + pageStartIndex = 1; + sizePerPage = 5; + }); + + it('should return correct page', () => { + expect(alignPage( + newDataSize, + prevDataSize, + currPage, + sizePerPage, + pageStartIndex + )).toEqual(pageStartIndex); + }); + }); + + describe('if partStartIndex is default 1', () => { + describe('and currPage is bigger than newest last page', () => { + beforeEach(() => { + newDataSize = 9; + prevDataSize = 12; + currPage = 3; + pageStartIndex = 1; + sizePerPage = 5; + }); + + it('should return correct page', () => { + expect(alignPage( + newDataSize, + prevDataSize, + currPage, + sizePerPage, + pageStartIndex + )).toEqual(2); + }); + }); + + describe('and currPage is short than newest last page', () => { + beforeEach(() => { + newDataSize = 11; + prevDataSize = 12; + currPage = 3; + pageStartIndex = 1; + sizePerPage = 5; + }); + + it('should return correct page', () => { + expect(alignPage( + newDataSize, + prevDataSize, + currPage, + sizePerPage, + pageStartIndex + )).toEqual(currPage); + }); + }); + }); + + describe('if partStartIndex is default 0', () => { + describe('and currPage is bigger than newest last page', () => { + beforeEach(() => { + newDataSize = 8; + prevDataSize = 11; + currPage = 2; + pageStartIndex = 0; + sizePerPage = 5; + }); + + it('should return correct page', () => { + expect(alignPage( + newDataSize, + prevDataSize, + currPage, + sizePerPage, + pageStartIndex + )).toEqual(1); + }); + }); + + describe('and currPage is short than newest last page', () => { + beforeEach(() => { + newDataSize = 11; + prevDataSize = 12; + currPage = 2; + pageStartIndex = 0; + sizePerPage = 5; + }); + + it('should return correct page', () => { + expect(alignPage( + newDataSize, + prevDataSize, + currPage, + sizePerPage, + pageStartIndex + )).toEqual(currPage); + }); }); }); }); diff --git a/packages/react-bootstrap-table2-toolkit/README.md b/packages/react-bootstrap-table2-toolkit/README.md index 78fb3a0..c85accc 100644 --- a/packages/react-bootstrap-table2-toolkit/README.md +++ b/packages/react-bootstrap-table2-toolkit/README.md @@ -67,6 +67,19 @@ const { SearchBar } = Search; 3. You should render `SearchBar` with `searchProps` as well. The position of `SearchBar` is depends on you. +### `SearchBar` Props +#### className - [string] +Custom the class on input element. + +#### placeholder - [string] +Custom the placeholder on input element. + +#### style - [object] +Custom the style on input element. + +#### delay = [number] +milionsecond for debounce user input. + ### Search Options #### defaultSearch - [string] @@ -127,6 +140,8 @@ const { SearchBar, ClearSearchButton } = Search; ``` +----- + ## Export CSV There are two steps to enable the export CSV functionality: @@ -176,6 +191,8 @@ Default is `true`. `false` will only export current data which display on table. #### onlyExportSelection - [bool] Default is `false`. `true` will only export the data which is selected. +----- + ## Column Toggle Let's see how to render the column toggle in your react component: diff --git a/packages/react-bootstrap-table2/src/body.js b/packages/react-bootstrap-table2/src/body.js index 2d445fc..a14e1b8 100644 --- a/packages/react-bootstrap-table2/src/body.js +++ b/packages/react-bootstrap-table2/src/body.js @@ -17,7 +17,6 @@ class Body extends React.Component { super(props); const { keyField, - visibleColumnSize, cellEdit, selectRow, expandRow @@ -34,7 +33,7 @@ class Body extends React.Component { const expandRowEnabled = !!expandRow.renderer; if (expandRowEnabled) { - RowComponent = withRowExpansion(RowAggregator, visibleColumnSize); + RowComponent = withRowExpansion(RowAggregator); } if (selectRowEnabled) { diff --git a/packages/react-bootstrap-table2/src/row-expand/row-consumer.js b/packages/react-bootstrap-table2/src/row-expand/row-consumer.js index 77f5e36..7578d6d 100644 --- a/packages/react-bootstrap-table2/src/row-expand/row-consumer.js +++ b/packages/react-bootstrap-table2/src/row-expand/row-consumer.js @@ -3,7 +3,7 @@ import React from 'react'; import ExpandRow from './expand-row'; import ExpansionContext from '../contexts/row-expand-context'; -export default (Component, visibleColumnSize) => { +export default (Component) => { const renderWithExpansion = (props, expandRow) => { const key = props.value; @@ -20,7 +20,7 @@ export default (Component, visibleColumnSize) => { />, expanded ? { expandRow.renderer(props.row) } : null