From c91f5219138619656e3d26b4171b92187f56c914 Mon Sep 17 00:00:00 2001 From: Allen Date: Sun, 25 Mar 2018 16:44:27 +0800 Subject: [PATCH 1/5] fix #258 (#268) --- docs/columns.md | 8 + .../react-bootstrap-table2-editor/README.md | 6 +- .../src/editing-cell.js | 21 ++- .../test/editing-cell.test.js | 141 +++++++++++++++++- .../examples/cell-edit/editor-class-table.js | 61 ++++++++ .../examples/cell-edit/editor-style-table.js | 69 +++++++++ .../stories/index.js | 8 +- .../react-bootstrap-table2/src/header-cell.js | 2 + packages/react-bootstrap-table2/src/row.js | 2 + 9 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 packages/react-bootstrap-table2-example/examples/cell-edit/editor-class-table.js create mode 100644 packages/react-bootstrap-table2-example/examples/cell-edit/editor-style-table.js diff --git a/docs/columns.md b/docs/columns.md index eeab42b..d7a7bea 100644 --- a/docs/columns.md +++ b/docs/columns.md @@ -32,6 +32,8 @@ Available properties in a column object: * [validator](#validator) * [editCellStyle](#editCellStyle) * [editCellClasses](#editCellClasses) +* [editorStyle](#editorStyle) +* [editorClasses](#editorClasses) * [filter](#filter) * [filterValue](#filterValue) @@ -552,6 +554,12 @@ Or take a callback function } ``` +## column.editorStyle - [Object | Function] +This is almost same as [`column.editCellStyle`](#editCellStyle), but `column.editorStyle` is custom the style on editor instead of cell(`td`). + +## column.editorClasses - [Object | Function] +This is almost same as [`column.editCellClasses`](#editCellClasses), but `column.editorClasses` is custom the class on editor instead of cell(`td`). + ## column.filter - [Object] Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters: diff --git a/packages/react-bootstrap-table2-editor/README.md b/packages/react-bootstrap-table2-editor/README.md index 02e0e46..5c9719e 100644 --- a/packages/react-bootstrap-table2-editor/README.md +++ b/packages/react-bootstrap-table2-editor/README.md @@ -49,13 +49,15 @@ How user save their new editings? We offer two ways: * Cell Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as a callback function) ## Customize Style/Class -Currently, we only support the editing cell style/class customization, in the future, we will offer more customizations. - ### Editing Cell * Customize the editing cell style via [column.editCellStyle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditcellstyle-object-function) * Customize the editing cell classname via [column.editCellClasses](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditcellclasses-string-function) +### Editor +* Customize the editor style via [column.editorStyle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorstyle-object-function) +* Customize the editor classname via [column.editoClasses](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorclasses-string-function) + ## Validation [`column.validator`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnvalidator-function) will help you to work on it! \ No newline at end of file diff --git a/packages/react-bootstrap-table2-editor/src/editing-cell.js b/packages/react-bootstrap-table2-editor/src/editing-cell.js index 5e54984..2c732de 100644 --- a/packages/react-bootstrap-table2-editor/src/editing-cell.js +++ b/packages/react-bootstrap-table2-editor/src/editing-cell.js @@ -14,7 +14,9 @@ export default _ => class EditingCell extends Component { static propTypes = { row: PropTypes.object.isRequired, + rowIndex: PropTypes.number.isRequired, column: PropTypes.object.isRequired, + columnIndex: PropTypes.number.isRequired, onUpdate: PropTypes.func.isRequired, onEscape: PropTypes.func.isRequired, timeToCloseMessage: PropTypes.number, @@ -123,7 +125,7 @@ export default _ => render() { const { invalidMessage } = this.state; - const { row, column, className, style } = this.props; + const { row, column, className, style, rowIndex, columnIndex } = this.props; const { dataField } = column; const value = _.get(row, dataField); @@ -133,7 +135,21 @@ export default _ => }; const hasError = _.isDefined(invalidMessage); - const editorClass = hasError ? cs('animated', 'shake') : null; + let customEditorClass = column.editorClasses || ''; + if (_.isFunction(column.editorClasses)) { + customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex); + } + + let editorStyle = column.editorStyle || {}; + if (_.isFunction(column.editorStyle)) { + editorStyle = column.editorStyle(value, row, rowIndex, columnIndex); + } + + const editorClass = cs({ + animated: hasError, + shake: hasError + }, customEditorClass); + return ( this.editor = node } defaultValue={ value } + style={ editorStyle } className={ editorClass } { ...editorAttrs } /> diff --git a/packages/react-bootstrap-table2-editor/test/editing-cell.test.js b/packages/react-bootstrap-table2-editor/test/editing-cell.test.js index 9b34d93..2741ee1 100644 --- a/packages/react-bootstrap-table2-editor/test/editing-cell.test.js +++ b/packages/react-bootstrap-table2-editor/test/editing-cell.test.js @@ -28,6 +28,9 @@ describe('EditingCell', () => { name: 'A' }; + const rowIndex = 1; + const columnIndex = 1; + let column = { dataField: 'id', text: 'ID' @@ -39,6 +42,8 @@ describe('EditingCell', () => { wrapper = shallow( { expect(textEditor.props().defaultValue).toEqual(row[column.dataField]); expect(textEditor.props().onKeyDown).toBeDefined(); expect(textEditor.props().onBlur).toBeDefined(); - expect(textEditor.props().className).toBeNull(); + expect(textEditor.props().className).toEqual(''); }); it('should not render EditorIndicator due to state.invalidMessage is null', () => { @@ -92,6 +97,8 @@ describe('EditingCell', () => { wrapper = shallow( { wrapper = shallow( { }); }); + describe('if column.editorClasses is defined', () => { + let columnWithEditorClasses; + const classes = 'test test1'; + + describe('and it is a function', () => { + beforeEach(() => { + columnWithEditorClasses = { + ...column, + editorClasses: jest.fn(() => classes) + }; + wrapper = shallow( + + ); + }); + + it('should render TextEditor with correct props', () => { + const textEditor = wrapper.find(TextEditor); + expect(textEditor.props().className).toEqual(classes); + }); + + it('should call column.editorClasses correctly', () => { + expect(columnWithEditorClasses.editorClasses).toHaveBeenCalledTimes(1); + expect(columnWithEditorClasses.editorClasses).toHaveBeenCalledWith( + _.get(row, column.dataField), + row, + rowIndex, + columnIndex + ); + }); + }); + + describe('and it is a string', () => { + beforeEach(() => { + columnWithEditorClasses = { + ...column, + editorClasses: classes + }; + wrapper = shallow( + + ); + }); + + it('should render TextEditor with correct props', () => { + const textEditor = wrapper.find(TextEditor); + expect(textEditor.props().className).toEqual(classes); + }); + }); + }); + + describe('if column.editorStyle is defined', () => { + let columnWithEditorStyle; + const style = { color: 'red' }; + + describe('and it is a function', () => { + beforeEach(() => { + columnWithEditorStyle = { + ...column, + editorStyle: jest.fn(() => style) + }; + wrapper = shallow( + + ); + }); + + it('should render TextEditor with correct props', () => { + const textEditor = wrapper.find(TextEditor); + expect(textEditor.props().style).toEqual(style); + }); + + it('should call column.editorStyle correctly', () => { + expect(columnWithEditorStyle.editorStyle).toHaveBeenCalledTimes(1); + expect(columnWithEditorStyle.editorStyle).toHaveBeenCalledWith( + _.get(row, column.dataField), + row, + rowIndex, + columnIndex + ); + }); + }); + + describe('and it is an object', () => { + beforeEach(() => { + columnWithEditorStyle = { + ...column, + editorStyle: style + }; + wrapper = shallow( + + ); + }); + + it('should render TextEditor with correct props', () => { + const textEditor = wrapper.find(TextEditor); + expect(textEditor.props().style).toEqual(style); + }); + }); + }); + describe('if blurToSave prop is true', () => { beforeEach(() => { wrapper = mount( { wrapper = mount( + (cell > 2101 ? 'editing-price-bigger-than-2101' : 'editing-price-small-than-2101') +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import cellEditFactory from 'react-bootstrap-table2-editor'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + editorClasses: 'editing-name' +}, { + dataField: 'price', + text: 'Product Price', + editorClasses: (cell, row, rowIndex, colIndex) => + (cell > 2101 ? 'editing-price-bigger-than-2101' : 'editing-price-small-than-2101') +}]; + + +`; + +export default () => ( +
+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/cell-edit/editor-style-table.js b/packages/react-bootstrap-table2-example/examples/cell-edit/editor-style-table.js new file mode 100644 index 0000000..37bf5bf --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/cell-edit/editor-style-table.js @@ -0,0 +1,69 @@ +/* eslint no-unused-vars: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import cellEditFactory from 'react-bootstrap-table2-editor'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const products = productsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + editorStyle: { + backgroundColor: '#20B2AA' + } +}, { + dataField: 'price', + text: 'Product Price', + editorStyle: (cell, row, rowIndex, colIndex) => { + const backgroundColor = cell > 2101 ? '#00BFFF' : '#00FFFF'; + return { backgroundColor }; + } +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import cellEditFactory from 'react-bootstrap-table2-editor'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + editorStyle: { + backgroundColor: '#20B2AA' + } +}, { + dataField: 'price', + text: 'Product Price', + editorStyle: (cell, row, rowIndex, colIndex) => { + const backgroundColor = cell > 2101 ? '#00BFFF' : '#00FFFF'; + return { backgroundColor }; + } +}]; + + +`; + +export default () => ( +
+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 3f116f5..ae50d60 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -73,6 +73,8 @@ import CellEditHooks from 'examples/cell-edit/cell-edit-hooks-table'; import CellEditValidator from 'examples/cell-edit/cell-edit-validator-table'; import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table'; import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table'; +import EditorStyleTable from 'examples/cell-edit/editor-style-table'; +import EditorClassTable from 'examples/cell-edit/editor-class-table'; // work on row selection import SingleSelectionTable from 'examples/row-selection/single-selection'; @@ -186,8 +188,10 @@ storiesOf('Cell Editing', module) .add('Cell Level Editable', () => ) .add('Rich Hook Functions', () => ) .add('Validation', () => ) - .add('Custom Cell Style When Editing', () => ) - .add('Custom Cell Classes When Editing', () => ); + .add('Custom Cell Style', () => ) + .add('Custom Cell Classes', () => ) + .add('Custom Editor Classes', () => ) + .add('Custom Editor Style', () => ); storiesOf('Row Selection', module) .add('Single Selection', () => ) diff --git a/packages/react-bootstrap-table2/src/header-cell.js b/packages/react-bootstrap-table2/src/header-cell.js index bb012d1..69fdfed 100644 --- a/packages/react-bootstrap-table2/src/header-cell.js +++ b/packages/react-bootstrap-table2/src/header-cell.js @@ -130,6 +130,8 @@ HeaderCell.propTypes = { editable: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]), editCellStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), editCellClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + editorStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), + editorClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), validator: PropTypes.func, filter: PropTypes.object, filterValue: PropTypes.func diff --git a/packages/react-bootstrap-table2/src/row.js b/packages/react-bootstrap-table2/src/row.js index 3efb0c5..b6222bb 100644 --- a/packages/react-bootstrap-table2/src/row.js +++ b/packages/react-bootstrap-table2/src/row.js @@ -79,7 +79,9 @@ class Row extends eventDelegater(Component) { Date: Sat, 31 Mar 2018 22:30:59 -0700 Subject: [PATCH 2/5] Fix textFilter() for Internet Explorer (includes() and find() are not supported) (#274) * Fix textFilter() for Internet Explorer 11 - replace includes() with indexOf() !== -1 - replace find() with for loop * Requested changes; more readability with for loop - use .length of the columns instead of the Object.keys() --- packages/react-bootstrap-table2-filter/src/filter.js | 10 ++++++++-- packages/react-bootstrap-table2/src/sort/wrapper.js | 9 +++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/react-bootstrap-table2-filter/src/filter.js b/packages/react-bootstrap-table2-filter/src/filter.js index f166742..e0d7a45 100644 --- a/packages/react-bootstrap-table2-filter/src/filter.js +++ b/packages/react-bootstrap-table2-filter/src/filter.js @@ -21,7 +21,7 @@ export const filterByText = _ => ( if (caseSensitive) { return cellStr.includes(filterVal); } - return cellStr.toLocaleUpperCase().includes(filterVal.toLocaleUpperCase()); + return cellStr.toLocaleUpperCase().indexOf(filterVal.toLocaleUpperCase()) !== -1; }); export const filterByNumber = _ => ( @@ -106,7 +106,13 @@ export const filters = (store, columns, _) => (currFilters) => { Object.keys(currFilters).forEach((dataField) => { const filterObj = currFilters[dataField]; filterFn = factory(filterObj.filterType); - const { filterValue } = columns.find(col => col.dataField === dataField); + let filterValue; + for (let i = 0; i < columns.length; i += 1) { + if (columns[i].dataField === dataField) { + filterValue = columns[i].filterValue; + break; + } + } result = filterFn(result, dataField, filterObj, filterValue); }); return result; diff --git a/packages/react-bootstrap-table2/src/sort/wrapper.js b/packages/react-bootstrap-table2/src/sort/wrapper.js index 71c5051..8528e0f 100644 --- a/packages/react-bootstrap-table2/src/sort/wrapper.js +++ b/packages/react-bootstrap-table2/src/sort/wrapper.js @@ -39,8 +39,13 @@ export default Base => } componentWillReceiveProps(nextProps) { - const sortedColumn = nextProps.columns.find( - column => column.dataField === nextProps.store.sortField); + let sortedColumn; + for (let i = 0; i < nextProps.columns.length; i += 1) { + if (nextProps.columns[i].dataField === nextProps.store.sortField) { + sortedColumn = nextProps.columns[i]; + break; + } + } if (sortedColumn && sortedColumn.sort) { nextProps.store.sortBy(sortedColumn); } From 41dc3ef6191e9f6d7528fffdb1a125a202c64dc0 Mon Sep 17 00:00:00 2001 From: Patrick O'Meara Date: Sun, 1 Apr 2018 15:32:24 +1000 Subject: [PATCH 3/5] empty noDataIndication when empty (#275) * don't display unneeded empty row when noDataIndication isn't set --- packages/react-bootstrap-table2/src/body.js | 3 +++ packages/react-bootstrap-table2/test/body.test.js | 8 +++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/react-bootstrap-table2/src/body.js b/packages/react-bootstrap-table2/src/body.js index 9ee9e89..b4ca400 100644 --- a/packages/react-bootstrap-table2/src/body.js +++ b/packages/react-bootstrap-table2/src/body.js @@ -35,6 +35,9 @@ const Body = (props) => { if (isEmpty) { const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication; + if (!indication) { + return null; + } content = ; } else { const nonEditableRows = cellEdit.nonEditableRows || []; diff --git a/packages/react-bootstrap-table2/test/body.test.js b/packages/react-bootstrap-table2/test/body.test.js index 5674b83..ea624e5 100644 --- a/packages/react-bootstrap-table2/test/body.test.js +++ b/packages/react-bootstrap-table2/test/body.test.js @@ -53,12 +53,10 @@ describe('Body', () => { />); }); - it('should render successfully', () => { + it('should not render', () => { expect(wrapper.length).toBe(1); - expect(wrapper.find('tbody').length).toBe(1); - expect(wrapper.find(RowSection).length).toBe(1); - expect(wrapper.find(RowSection).prop('colSpan')).toBe(columns.length); - expect(wrapper.find(RowSection).prop('content')).toBe(null); + expect(wrapper.find('tbody').length).toBe(0); + expect(wrapper.find(RowSection).length).toBe(0); }); describe('when noDataIndication props is defined', () => { From 11d4f40089879e5e4f830c0e4f5c76c9a9c064a3 Mon Sep 17 00:00:00 2001 From: Patrick O'Meara Date: Sun, 1 Apr 2018 15:34:06 +1000 Subject: [PATCH 4/5] noDataIndication (#276) * use the correct amount of cells when the first row is select * storybook added for development, not necessary in docs fixes react-bootstrap-table/react-bootstrap-table2#264 --- .../row-selection/selection-no-data.js | 62 +++++++++++++++++++ .../stories/index.js | 2 + .../src/props-resolver/column-resolver.js | 9 ++- .../src/props-resolver/index.js | 6 +- .../test/props-resolver/index.test.js | 4 +- 5 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 packages/react-bootstrap-table2-example/examples/row-selection/selection-no-data.js diff --git a/packages/react-bootstrap-table2-example/examples/row-selection/selection-no-data.js b/packages/react-bootstrap-table2-example/examples/row-selection/selection-no-data.js new file mode 100644 index 0000000..4867ffd --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/selection-no-data.js @@ -0,0 +1,62 @@ +/* eslint no-unused-vars: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import Code from 'components/common/code-block'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const selectRow1 = { + mode: 'checkbox', + clickToSelect: true +}; + +const sourceCode1 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const selectRow = { + mode: 'checkbox', + clickToSelect: true +}; + + +`; + +export default () => ( +
+ + { sourceCode1 } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index ae50d60..206f1ee 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -83,6 +83,7 @@ import ClickToSelectTable from 'examples/row-selection/click-to-select'; import DefaultSelectTable from 'examples/row-selection/default-select'; import SelectionManagement from 'examples/row-selection/selection-management'; import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit'; +import SelectionNoDataTable from 'examples/row-selection/selection-no-data'; import SelectionStyleTable from 'examples/row-selection/selection-style'; import SelectionClassTable from 'examples/row-selection/selection-class'; import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows'; @@ -200,6 +201,7 @@ storiesOf('Row Selection', module) .add('Default Select', () => ) .add('Selection Management', () => ) .add('Click to Select and Edit Cell', () => ) + .add('Selection without Data', () => ) .add('Selection Style', () => ) .add('Selection Class', () => ) .add('Selection Background Color', () => ) diff --git a/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js b/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js index 5846498..c330c7d 100644 --- a/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js +++ b/packages/react-bootstrap-table2/src/props-resolver/column-resolver.js @@ -1,6 +1,11 @@ export default ExtendBase => class ColumnResolver extends ExtendBase { - visibleColumnSize() { - return this.props.columns.filter(c => !c.hidden).length; + visibleColumnSize(includeSelectColumn = true) { + const columnLen = this.props.columns.filter(c => !c.hidden).length; + if (!includeSelectColumn) return columnLen; + if (this.props.selectRow && !this.props.selectRow.hideSelectColumn) { + return columnLen + 1; + } + return columnLen; } }; diff --git a/packages/react-bootstrap-table2/src/props-resolver/index.js b/packages/react-bootstrap-table2/src/props-resolver/index.js index 4871e2d..4f40d97 100644 --- a/packages/react-bootstrap-table2/src/props-resolver/index.js +++ b/packages/react-bootstrap-table2/src/props-resolver/index.js @@ -5,12 +5,12 @@ import _ from '../utils'; export default ExtendBase => class TableResolver extends ColumnResolver(ExtendBase) { validateProps() { - const { columns, keyField } = this.props; + const { keyField } = this.props; if (!keyField) { throw new Error('Please specify a field as key via keyField'); } - if (this.visibleColumnSize(columns) <= 0) { - throw new Error('No any visible columns detect'); + if (this.visibleColumnSize(false) <= 0) { + throw new Error('No visible columns detected'); } } diff --git a/packages/react-bootstrap-table2/test/props-resolver/index.test.js b/packages/react-bootstrap-table2/test/props-resolver/index.test.js index 1b6bf8c..cb3a54b 100644 --- a/packages/react-bootstrap-table2/test/props-resolver/index.test.js +++ b/packages/react-bootstrap-table2/test/props-resolver/index.test.js @@ -56,7 +56,7 @@ describe('TableResolver', () => { }); }); - describe('if columns is all unvisible', () => { + describe('if no columns are visible', () => { beforeEach(() => { const mockElement = React.createElement(BootstrapTableMock, { data, keyField, columns: [] @@ -67,7 +67,7 @@ describe('TableResolver', () => { it('should throw error', () => { expect(() => wrapper.instance().validateProps() - ).toThrow(new Error('No any visible columns detect')); + ).toThrow(new Error('No visible columns detected')); }); }); }); From 88e1a0774b91308bbb265209fe178ca082a8d5e3 Mon Sep 17 00:00:00 2001 From: Allen Date: Sun, 1 Apr 2018 14:14:32 +0800 Subject: [PATCH 5/5] fix #281 --- docs/README.md | 4 ++ .../examples/sort/default-sort-direction.js | 67 +++++++++++++++++++ .../stories/index.js | 2 + .../src/bootstrap-table.js | 1 + .../src/sort/wrapper.js | 6 +- .../react-bootstrap-table2/src/store/index.js | 4 +- .../react-bootstrap-table2/src/store/sort.js | 4 +- .../test/store/index.test.js | 7 +- .../test/store/sort.test.js | 4 ++ 9 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 packages/react-bootstrap-table2-example/examples/sort/default-sort-direction.js diff --git a/docs/README.md b/docs/README.md index 34b38df..181b4a8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,7 @@ * [rowClasses](#rowClasses) * [rowEvents](#rowEvents) * [defaultSorted](#defaultSorted) +* [defaultSortDirection](#defaultSortDirection) * [pagination](#pagination) * [filter](#filter) * [onTableChange](#onTableChange) @@ -168,6 +169,9 @@ const defaultSorted = [{ }]; ``` +### defaultSortDirection - [String] +Default sort direction when user click on header column at first time, available value is `asc` and `desc`. Default is `desc`. + ### pagination - [Object] `pagination` allow user to render a pagination panel on the bottom of table. But pagination functionality is separated from core of `react-bootstrap-table2` so that you are suppose to install `react-bootstrap-table2-paginator` additionally. diff --git a/packages/react-bootstrap-table2-example/examples/sort/default-sort-direction.js b/packages/react-bootstrap-table2-example/examples/sort/default-sort-direction.js new file mode 100644 index 0000000..f5c0aea --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/sort/default-sort-direction.js @@ -0,0 +1,67 @@ +/* eslint react/prefer-stateless-function: 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 +}, { + dataField: 'price', + text: 'Product Price', + sort: true +}]; + + +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 +}, { + dataField: 'price', + text: 'Product Price', + sort: true +}]; + +const defaultSorted = [{ + dataField: 'name', + order: 'desc' +}]; + + +`; + + +class DefaultSortDirectionTable extends React.PureComponent { + render() { + return ( +
+ + { sourceCode } +
+ ); + } +} + +export default DefaultSortDirectionTable; diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 206f1ee..25d8538 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -57,6 +57,7 @@ import RowEventTable from 'examples/rows/row-event'; // table sort import EnableSortTable from 'examples/sort/enable-sort-table'; 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 HeaderSortingClassesTable from 'examples/sort/header-sorting-classes'; @@ -175,6 +176,7 @@ storiesOf('Work on Rows', module) storiesOf('Sort Table', module) .add('Enable Sort', () => ) .add('Default Sort Table', () => ) + .add('Default Sort Direction Table', () => ) .add('Sort Events', () => ) .add('Custom Sort Fuction', () => ) .add('Custom Classes on Sorting Header Column', () => ) diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index 743eb3b..0e801cb 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -149,6 +149,7 @@ BootstrapTable.propTypes = { dataField: PropTypes.string.isRequired, order: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]).isRequired })), + defaultSortDirection: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]), overlay: PropTypes.func, onTableChange: PropTypes.func, onSort: PropTypes.func, diff --git a/packages/react-bootstrap-table2/src/sort/wrapper.js b/packages/react-bootstrap-table2/src/sort/wrapper.js index 8528e0f..d20d207 100644 --- a/packages/react-bootstrap-table2/src/sort/wrapper.js +++ b/packages/react-bootstrap-table2/src/sort/wrapper.js @@ -15,7 +15,7 @@ export default Base => } componentWillMount() { - const { columns, defaultSorted, store } = this.props; + const { columns, defaultSorted, defaultSortDirection, store } = this.props; // defaultSorted is an array, it's ready to use as multi / single sort // when we start to support multi sort, please update following code to use array.forEach if (defaultSorted && defaultSorted.length > 0) { @@ -23,7 +23,7 @@ export default Base => const order = defaultSorted[0].order; const column = columns.filter(col => col.dataField === dataField); if (column.length > 0) { - store.setSort(column[0], order); + store.setSort(column[0], order, defaultSortDirection); if (column[0].onSort) { column[0].onSort(store.sortField, store.sortOrder); @@ -53,7 +53,7 @@ export default Base => handleSort(column) { const { store } = this.props; - store.setSort(column); + store.setSort(column, undefined, this.props.defaultSortDirection); if (column.onSort) { column.onSort(store.sortField, store.sortOrder); diff --git a/packages/react-bootstrap-table2/src/store/index.js b/packages/react-bootstrap-table2/src/store/index.js index 6711c99..f0d7933 100644 --- a/packages/react-bootstrap-table2/src/store/index.js +++ b/packages/react-bootstrap-table2/src/store/index.js @@ -21,8 +21,8 @@ export default class Store { if (row) _.set(row, dataField, newValue); } - setSort({ dataField }, order) { - this.sortOrder = nextOrder(this)(dataField, order); + setSort({ dataField }, order, defaultOrder) { + this.sortOrder = nextOrder(this)(dataField, order, defaultOrder); this.sortField = dataField; } diff --git a/packages/react-bootstrap-table2/src/store/sort.js b/packages/react-bootstrap-table2/src/store/sort.js index a2228bb..1987e0e 100644 --- a/packages/react-bootstrap-table2/src/store/sort.js +++ b/packages/react-bootstrap-table2/src/store/sort.js @@ -37,11 +37,11 @@ export const sort = ({ data, sortOrder, sortField }) => (sortFunc) => { return _data; }; -export const nextOrder = store => (field, order) => { +export const nextOrder = store => (field, order, defaultOrder = Const.SORT_DESC) => { if (order) return order; if (field !== store.sortField) { - return Const.SORT_DESC; + return defaultOrder; } return store.sortOrder === Const.SORT_DESC ? Const.SORT_ASC : Const.SORT_DESC; }; diff --git a/packages/react-bootstrap-table2/test/store/index.test.js b/packages/react-bootstrap-table2/test/store/index.test.js index dc8b28e..a257272 100644 --- a/packages/react-bootstrap-table2/test/store/index.test.js +++ b/packages/react-bootstrap-table2/test/store/index.test.js @@ -56,10 +56,15 @@ describe('Store Base', () => { expect(store.sortOrder).toEqual(Const.SORT_DESC); }); - it('should force assign sortOrder correctly if second argument is passed', () => { + it('should force assign sortOrder correctly if second argument is given', () => { store.setSort({ dataField }, Const.SORT_DESC); expect(store.sortOrder).toEqual(Const.SORT_DESC); }); + + it('should force assign sortOrder correctly if third argument is given', () => { + store.setSort({ dataField }, undefined, Const.SORT_ASC); + expect(store.sortOrder).toEqual(Const.SORT_ASC); + }); }); describe('sortBy', () => { diff --git a/packages/react-bootstrap-table2/test/store/sort.test.js b/packages/react-bootstrap-table2/test/store/sort.test.js index 3279cd5..33422b9 100644 --- a/packages/react-bootstrap-table2/test/store/sort.test.js +++ b/packages/react-bootstrap-table2/test/store/sort.test.js @@ -63,6 +63,10 @@ describe('Sort Function', () => { expect(nextOrder(store)('name')).toBe(Const.SORT_DESC); }); + it('should return correcly order when store.sortField is not eq next sort field and default sort direction is given', () => { + expect(nextOrder(store)('name', undefined, Const.SORT_ASC)).toBe(Const.SORT_ASC); + }); + it('should return correcly order when store.sortField is eq next sort field', () => { store.sortField = 'name'; store.sortOrder = Const.SORT_DESC;