diff --git a/docs/columns.md b/docs/columns.md index afeffe7..5bb23a6 100644 --- a/docs/columns.md +++ b/docs/columns.md @@ -13,6 +13,7 @@ Available properties in a column object: * [formatExtraData](#formatExtraData) * [sort](#sort) * [sortFunc](#sortFunc) +* [sortCaret](#sortCaret) * [onSort](#onSort) * [classes](#classes) * [style](#style) @@ -154,6 +155,20 @@ Enable the column sort via a `true` value given. } ``` +## column.sortCaret - [Function] +Use`column.sortCaret` to custom the sort caret. This callback function accept two arguments: `order` and `column` + +```js +{ + // omit... + sort: true, + sortCaret: (order, column) => { + return //... + } +} +``` +> The possible value of `order` argument is **`asc`**, **`desc`** and **`undefined`**. + ## column.classes - [String | Function] It's available to have custom class on table column: diff --git a/packages/react-bootstrap-table2-example/examples/basic/exposed-function.js b/packages/react-bootstrap-table2-example/examples/basic/exposed-function.js new file mode 100644 index 0000000..6ea656d --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/basic/exposed-function.js @@ -0,0 +1,182 @@ +/* eslint no-return-assign: 0 */ +/* eslint no-console: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import paginationFactory from 'react-bootstrap-table2-paginator'; +import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const products = productsGenerator(63); + +const columns = [{ + dataField: 'id', + text: 'Product ID', + sort: true +}, { + dataField: 'name', + text: 'Product Name', + sort: true, + filter: textFilter() +}, { + dataField: 'price', + text: 'Product Price', + sort: true, + filter: textFilter() +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +class ExposedFunctionTable extends React.Component { + handleGetCurrentData = () => { + console.log(this.node.table.props.data); + } + + handleGetSelectedData = () => { + console.log(this.node.selectionContext.state.selected); + } + + handleGetExpandedData = () => { + console.log(this.node.rowExpandContext.state.expanded); + } + + handleGetCurrentPage = () => { + console.log(this.node.paginationContext.currPage); + } + + handleGetCurrentSizePerPage = () => { + console.log(this.node.paginationContext.currSizePerPage); + } + + handleGetCurrentSortColumn = () => { + console.log(this.node.sortContext.state.sortColumn); + } + + handleGetCurrentSortOrder = () => { + console.log(this.node.sortContext.state.sortOrder); + } + + handleGetCurrentFilter = () => { + console.log(this.node.filterContext.currFilters); + } + + render() { + const expandRow = { + renderer: row => ( +
+

.....

+

You can render anything here, also you can add additional data on every row object

+

expandRow.renderer callback will pass the origin row object to you

+
+ ), + showExpandColumn: true + }; + return ( +
+ + + + + + + + + this.node = n } + keyField="id" + data={ products } + columns={ columns } + filter={ filterFactory() } + pagination={ paginationFactory() } + selectRow={ { mode: 'checkbox', clickToSelect: true } } + expandRow={ expandRow } + /> + { sourceCode } +
+ ); + } +} +`; + +export default class ExposedFunctionTable extends React.Component { + handleGetCurrentData = () => { + console.log(this.node.table.props.data); + } + + handleGetSelectedData = () => { + console.log(this.node.selectionContext.state.selected); + } + + handleGetExpandedData = () => { + console.log(this.node.rowExpandContext.state.expanded); + } + + handleGetCurrentPage = () => { + console.log(this.node.paginationContext.currPage); + } + + handleGetCurrentSizePerPage = () => { + console.log(this.node.paginationContext.currSizePerPage); + } + + handleGetCurrentSortColumn = () => { + console.log(this.node.sortContext.state.sortColumn); + } + + handleGetCurrentSortOrder = () => { + console.log(this.node.sortContext.state.sortOrder); + } + + handleGetCurrentFilter = () => { + console.log(this.node.filterContext.currFilters); + } + + render() { + const expandRow = { + renderer: row => ( +
+

{ `This Expand row is belong to rowKey ${row.id}` }

+

You can render anything here, also you can add additional data on every row object

+

expandRow.renderer callback will pass the origin row object to you

+
+ ), + showExpandColumn: true + }; + return ( +
+ + + + + + + + + this.node = n } + keyField="id" + data={ products } + columns={ columns } + filter={ filterFactory() } + pagination={ paginationFactory() } + selectRow={ { mode: 'checkbox', clickToSelect: true } } + expandRow={ expandRow } + /> + { sourceCode } +
+ ); + } +} diff --git a/packages/react-bootstrap-table2-example/examples/sort/custom-sort-caret.js b/packages/react-bootstrap-table2-example/examples/sort/custom-sort-caret.js new file mode 100644 index 0000000..4a1a650 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/sort/custom-sort-caret.js @@ -0,0 +1,59 @@ +/* eslint no-unused-vars: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const products = productsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID', + sort: true +}, { + dataField: 'name', + text: 'Product Name', + sort: true, + sortCaret: (order, column) => { + if (!order) return (  Desc/Asc); + else if (order === 'asc') return (  Desc/Asc); + else if (order === 'desc') return (  Desc/Asc); + return null; + } +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = [{ + dataField: 'id', + text: 'Product ID', + sort: true +}, { + dataField: 'name', + text: 'Product Name', + sort: true, + sortCaret: (order, column) => { + if (!order) return (  Desc/Asc); + else if (order === 'asc') return (  Desc/Asc); + else if (order === 'desc') return (  Desc/Asc); + return null; + } +}, { + dataField: 'price', + text: 'Product Price' +}]; + + +`; + +export default () => ( +
+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 771655f..2941f37 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -13,6 +13,7 @@ import NoDataTable from 'examples/basic/no-data-table'; import CustomizedIdClassesTable from 'examples/basic/customized-id-classes'; import CaptionTable from 'examples/basic/caption-table'; import LargeTable from 'examples/basic/large-table'; +import ExposedAPITable from 'examples/basic/exposed-function'; // bootstrap 4 import Bootstrap4DefaultSortTable from 'examples/bootstrap4/sort'; @@ -83,6 +84,7 @@ import DefaultSortTable from 'examples/sort/default-sort-table'; import DefaultSortDirectionTable from 'examples/sort/default-sort-direction'; import SortEvents from 'examples/sort/sort-events'; import CustomSortTable from 'examples/sort/custom-sort-table'; +import CustomSortCaretTable from 'examples/sort/custom-sort-caret'; import HeaderSortingClassesTable from 'examples/sort/header-sorting-classes'; import HeaderSortingStyleTable from 'examples/sort/header-sorting-style'; @@ -189,7 +191,8 @@ storiesOf('Basic Table', module) .add('Indication For Empty Table', () => ) .add('Customized id and class table', () => ) .add('Table with caption', () => ) - .add('Large Table', () => ); + .add('Large Table', () => ) + .add('Exposed API', () => ); storiesOf('Bootstrap 4', module) .addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR)) @@ -267,6 +270,7 @@ storiesOf('Sort Table', module) .add('Default Sort Direction Table', () => ) .add('Sort Events', () => ) .add('Custom Sort Fuction', () => ) + .add('Custom Sort Caret', () => ) .add('Custom Classes on Sorting Header Column', () => ) .add('Custom Style on Sorting Header Column', () => ); diff --git a/packages/react-bootstrap-table2-paginator/src/context.js b/packages/react-bootstrap-table2-paginator/src/context.js index eb58ceb..d5ffb96 100644 --- a/packages/react-bootstrap-table2-paginator/src/context.js +++ b/packages/react-bootstrap-table2-paginator/src/context.js @@ -68,6 +68,9 @@ export default ( currPage = newPage; needNewState = true; } + } else { + this.currPage = nextProps.pagination.options.page; + this.currSizePerPage = nextProps.pagination.options.sizePerPage; } if (needNewState) { diff --git a/packages/react-bootstrap-table2-paginator/test/context.test.js b/packages/react-bootstrap-table2-paginator/test/context.test.js index 9d13658..d72caea 100644 --- a/packages/react-bootstrap-table2-paginator/test/context.test.js +++ b/packages/react-bootstrap-table2-paginator/test/context.test.js @@ -160,6 +160,27 @@ describe('PaginationContext', () => { }); }); + describe('when remote pagination is enable', () => { + beforeEach(() => { + wrapper = shallow(shallowContext({ ...defaultPagination }, true)); + instance = wrapper.instance(); + wrapper.render(); + nextProps = { + data, + pagination: { ...defaultPagination, options: { page: 3, sizePerPage: 5 } } + }; + instance.componentWillReceiveProps(nextProps); + }); + + it('should always set currPage from nextProps.pagination.options.page', () => { + expect(instance.currPage).toEqual(nextProps.pagination.options.page); + }); + + it('should always set currSizePerPage from nextProps.pagination.options.sizePerPage', () => { + expect(instance.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage); + }); + }); + describe('when page is not align', () => { beforeEach(() => { wrapper = shallow(shallowContext({ diff --git a/packages/react-bootstrap-table2-toolkit/src/search/context.js b/packages/react-bootstrap-table2-toolkit/src/search/context.js index c579999..90484bf 100644 --- a/packages/react-bootstrap-table2-toolkit/src/search/context.js +++ b/packages/react-bootstrap-table2-toolkit/src/search/context.js @@ -39,8 +39,10 @@ export default (options = { const { data, columns } = this.props; let { searchText } = this.props; - if (isRemoteSearch() && this.performRemoteSearch) { - handleRemoteSearchChange(searchText); + if (isRemoteSearch()) { + if (this.performRemoteSearch) { + handleRemoteSearchChange(searchText); + } return data; } diff --git a/packages/react-bootstrap-table2/src/contexts/index.js b/packages/react-bootstrap-table2/src/contexts/index.js index 60c3a55..72436ce 100644 --- a/packages/react-bootstrap-table2/src/contexts/index.js +++ b/packages/react-bootstrap-table2/src/contexts/index.js @@ -66,6 +66,7 @@ const withContext = Base => selectionProps ) => ( this.table = n } { ...this.props } { ...selectionProps } { ...sortProps } @@ -91,6 +92,7 @@ const withContext = Base => ) => ( this.selectionContext = n } selectRow={ this.props.selectRow } data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) } > @@ -123,6 +125,7 @@ const withContext = Base => ) => ( this.rowExpandContext = n } expandRow={ this.props.expandRow } data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) } > diff --git a/packages/react-bootstrap-table2/src/header-cell.js b/packages/react-bootstrap-table2/src/header-cell.js index 79b4b9b..1270082 100644 --- a/packages/react-bootstrap-table2/src/header-cell.js +++ b/packages/react-bootstrap-table2/src/header-cell.js @@ -24,6 +24,7 @@ const HeaderCell = (props) => { const { text, sort, + sortCaret, filter, filterRenderer, headerTitle, @@ -69,7 +70,7 @@ const HeaderCell = (props) => { cellAttrs.className = cs(cellAttrs.className, 'sortable'); if (sorting) { - sortSymbol = ; + sortSymbol = sortCaret ? sortCaret(sortOrder, column) : ; // append customized classes or style if table was sorting based on the current column. cellClasses = cs( @@ -86,7 +87,7 @@ const HeaderCell = (props) => { : headerSortingStyle }; } else { - sortSymbol = ; + sortSymbol = sortCaret ? sortCaret(undefined, column) : ; } } @@ -151,6 +152,7 @@ HeaderCell.propTypes = { onSort: PropTypes.func, sorting: PropTypes.bool, sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]), + sortCaret: PropTypes.func, isLastSorting: PropTypes.bool, onFilter: PropTypes.func, onExternalFilter: PropTypes.func diff --git a/packages/react-bootstrap-table2/test/header-cell.test.js b/packages/react-bootstrap-table2/test/header-cell.test.js index 6814135..b0c4525 100644 --- a/packages/react-bootstrap-table2/test/header-cell.test.js +++ b/packages/react-bootstrap-table2/test/header-cell.test.js @@ -403,6 +403,24 @@ describe('HeaderCell', () => { it('header should render SortSymbol as default', () => { expect(wrapper.find(SortSymbol).length).toBe(1); }); + + describe('when sortCaret is defined ', () => { + beforeEach(() => { + column = { ...column, sortCaret: jest.fn() }; + wrapper = shallow( + + ); + }); + + it('header should not render SortSymbol', () => { + expect(wrapper.find(SortSymbol).length).toBe(0); + }); + + it('should call column.sortCaret correctly', () => { + expect(column.sortCaret).toHaveBeenCalledTimes(1); + expect(column.sortCaret).toHaveBeenCalledWith(undefined, column); + }); + }); }); describe('and sorting prop is true', () => { @@ -420,6 +438,30 @@ describe('HeaderCell', () => { }); }); + describe('when sortCaret is defined ', () => { + beforeEach(() => { + column = { ...column, sortCaret: jest.fn() }; + wrapper = shallow( + + ); + }); + + it('header should not render SortSymbol', () => { + expect(wrapper.find(SortSymbol).length).toBe(0); + }); + + it('should call column.sortCaret correctly', () => { + expect(column.sortCaret).toHaveBeenCalledTimes(1); + expect(column.sortCaret).toHaveBeenCalledWith(Const.SORT_ASC, column); + }); + }); + describe('when headerSortingClasses is defined ', () => { const classes = 'foo'; const order = Const.SORT_DESC;