diff --git a/packages/react-bootstrap-table2-paginator/src/page.js b/packages/react-bootstrap-table2-paginator/src/page.js new file mode 100644 index 0000000..c18ede9 --- /dev/null +++ b/packages/react-bootstrap-table2-paginator/src/page.js @@ -0,0 +1,16 @@ +export const getByCurrPage = store => (page, sizePerPage, pageStartIndex) => { + const getNormalizedPage = () => { + const offset = Math.abs(1 - pageStartIndex); + return page + offset; + }; + const end = (getNormalizedPage() * sizePerPage) - 1; + const start = end - (sizePerPage - 1); + const dataSize = store.data.length; + + const result = []; + for (let i = start; i <= end; i += 1) { + result.push(store.data[i]); + if (i + 1 === dataSize) break; + } + return result; +}; diff --git a/packages/react-bootstrap-table2-paginator/src/wrapper.js b/packages/react-bootstrap-table2-paginator/src/wrapper.js index 0adc3c9..349e7a8 100644 --- a/packages/react-bootstrap-table2-paginator/src/wrapper.js +++ b/packages/react-bootstrap-table2-paginator/src/wrapper.js @@ -5,6 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Const from './const'; +import { getByCurrPage } from './page'; const wrapperFactory = baseElement => class PaginationWrapper extends Component { @@ -98,7 +99,7 @@ const wrapperFactory = baseElement => const data = this.isRemote() ? this.props.data : - store.getByCurrPage(currPage, currSizePerPage, pageStartIndex); + getByCurrPage(store)(currPage, currSizePerPage, pageStartIndex); const base = baseElement({ ...this.props, @@ -110,7 +111,7 @@ const wrapperFactory = baseElement => base, { + let data; + let store; + + describe('getByCurrPage', () => { + beforeEach(() => { + data = []; + for (let i = 0; i < 100; i += 1) { + data.push({ id: i, name: `test_name${i}` }); + } + store = new Store('id'); + store.data = data; + }); + + it('should always return correct data', () => { + [ + // [page, sizePerPage, pageStartIndex] + [1, 10, 1], + [1, 25, 1], + [1, 30, 1], + [3, 30, 1], + [4, 30, 1], + [10, 10, 1], + [0, 10, 0], + [1, 10, 0], + [9, 10, 0] + ].forEach(([page, sizePerPage, pageStartIndex]) => { + const rows = getByCurrPage(store)(page, sizePerPage, pageStartIndex); + expect(rows).toBeDefined(); + expect(Array.isArray(rows)).toBeTruthy(); + expect(rows.every(row => !!row)).toBeTruthy(); + }); + }); + }); +}); diff --git a/packages/react-bootstrap-table2-paginator/test/wrapper.test.js b/packages/react-bootstrap-table2-paginator/test/wrapper.test.js index d7ec0bb..c06a230 100644 --- a/packages/react-bootstrap-table2-paginator/test/wrapper.test.js +++ b/packages/react-bootstrap-table2-paginator/test/wrapper.test.js @@ -4,7 +4,7 @@ import { shallow } from 'enzyme'; import BootstrapTable from 'react-bootstrap-table2/src/bootstrap-table'; -import Store from 'react-bootstrap-table2/src/store/base'; +import Store from 'react-bootstrap-table2/src/store'; import paginator from '../src'; import wrapperFactory from '../src/wrapper'; import Pagination from '../src/pagination'; @@ -23,19 +23,23 @@ describe('Wrapper', () => { let wrapper; let instance; - const createTableProps = (props = {}) => ({ - keyField: 'id', - columns: [{ - dataField: 'id', - text: 'ID' - }, { - dataField: 'name', - text: 'Name' - }], - data, - pagination: paginator(props.options), - store: new Store({ data }) - }); + const createTableProps = (props = {}) => { + const tableProps = { + keyField: 'id', + columns: [{ + dataField: 'id', + text: 'ID' + }, { + dataField: 'name', + text: 'Name' + }], + data, + pagination: paginator(props.options), + store: new Store('id') + }; + tableProps.store.data = data; + return tableProps; + }; const pureTable = props => (); @@ -76,7 +80,7 @@ describe('Wrapper', () => { it('should rendering Pagination correctly', () => { const pagination = wrapper.find(Pagination); expect(pagination.length).toBe(1); - expect(pagination.prop('dataSize')).toEqual(props.store.getDataSize()); + expect(pagination.prop('dataSize')).toEqual(props.store.data.length); expect(pagination.prop('currPage')).toEqual(instance.state.currPage); expect(pagination.prop('currSizePerPage')).toEqual(instance.state.currSizePerPage); expect(pagination.prop('onPageChange')).toEqual(instance.handleChangePage); diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index 89f2bd8..ca7000c 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -9,6 +9,7 @@ import Caption from './caption'; import Body from './body'; import PropsBaseResolver from './props-resolver'; import Const from './const'; +import { isSelectedAll } from './store/selection'; class BootstrapTable extends PropsBaseResolver(Component) { constructor(props) { @@ -73,7 +74,7 @@ class BootstrapTable extends PropsBaseResolver(Component) { const headerCellSelectionInfo = this.resolveSelectRowPropsForHeader({ onAllRowsSelect: this.props.onAllRowsSelect, selected: store.selected, - allRowsSelected: store.isAllRowsSelected() + allRowsSelected: isSelectedAll(store) }); return ( @@ -96,7 +97,7 @@ class BootstrapTable extends PropsBaseResolver(Component) { noDataIndication={ noDataIndication } cellEdit={ cellEditInfo } selectRow={ cellSelectionInfo } - selectedRowKeys={ store.getSelectedRowKeys() } + selectedRowKeys={ store.selected } rowStyle={ rowStyle } rowClasses={ rowClasses } rowEvents={ rowEvents } diff --git a/packages/react-bootstrap-table2/src/container.js b/packages/react-bootstrap-table2/src/container.js index 0236b9c..15d731e 100644 --- a/packages/react-bootstrap-table2/src/container.js +++ b/packages/react-bootstrap-table2/src/container.js @@ -1,7 +1,7 @@ /* eslint no-return-assign: 0 */ /* eslint react/prop-types: 0 */ import React, { Component } from 'react'; -import Store from './store/base'; +import Store from './store'; import { wrapWithCellEdit, @@ -16,13 +16,14 @@ const withDataStore = Base => class BootstrapTableContainer extends Component { constructor(props) { super(props); - this.store = new Store(props); + this.store = new Store(props.keyField); + this.store.data = props.data; this.handleUpdateCell = this.handleUpdateCell.bind(this); this.onRemotePageChange = this.onRemotePageChange.bind(this); } componentWillReceiveProps(nextProps) { - this.store.set(nextProps.data); + this.store.data = nextProps.data; } onRemotePageChange(page, sizePerPage) { diff --git a/packages/react-bootstrap-table2/src/row-selection/wrapper.js b/packages/react-bootstrap-table2/src/row-selection/wrapper.js index 0e6b1c7..3a73e95 100644 --- a/packages/react-bootstrap-table2/src/row-selection/wrapper.js +++ b/packages/react-bootstrap-table2/src/row-selection/wrapper.js @@ -5,6 +5,13 @@ import PropTypes from 'prop-types'; import { selectionElement } from '../table-factory'; import Const from '../const'; +import { + isAnySelectedRow, + selectableKeys, + unSelectableKeys, + getSelectedRows +} from '../store/selection'; +import { getRowByRowId } from '../store/rows'; class RowSelectionWrapper extends Component { constructor(props) { @@ -12,7 +19,7 @@ class RowSelectionWrapper extends Component { this.handleRowSelect = this.handleRowSelect.bind(this); this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this); this.state = { - selectedRowKeys: props.store.getSelectedRowKeys() + selectedRowKeys: props.store.selected }; } @@ -25,7 +32,7 @@ class RowSelectionWrapper extends Component { const { selectRow: { mode, onSelect }, store } = this.props; const { ROW_SELECT_SINGLE } = Const; - let currSelected = [...store.getSelectedRowKeys()]; + let currSelected = [...store.selected]; if (mode === ROW_SELECT_SINGLE) { // when select mode is radio currSelected = [rowKey]; @@ -35,10 +42,10 @@ class RowSelectionWrapper extends Component { currSelected = currSelected.filter(value => value !== rowKey); } - store.setSelectedRowKeys(currSelected); + store.selected = currSelected; if (onSelect) { - const row = store.getRowByRowId(rowKey); + const row = getRowByRowId(store)(rowKey); onSelect(row, checked, rowIndex); } @@ -56,20 +63,20 @@ class RowSelectionWrapper extends Component { onSelectAll, nonSelectable } } = this.props; - const selected = store.isAnySelectedRow(nonSelectable); + const selected = isAnySelectedRow(store)(nonSelectable); // set next status of all row selected by store.selected or customizing by user. const result = option || !selected; const currSelected = result ? - store.selectAllRows(nonSelectable) : - store.cleanSelectedRows(nonSelectable); + selectableKeys(store)(nonSelectable) : + unSelectableKeys(store)(nonSelectable); - store.setSelectedRowKeys(currSelected); + store.selected = currSelected; if (onSelectAll) { - onSelectAll(result, store.getSelectedRows()); + onSelectAll(result, getSelectedRows(store)); } this.setState(() => ({ diff --git a/packages/react-bootstrap-table2/src/sort/wrapper.js b/packages/react-bootstrap-table2/src/sort/wrapper.js index b3e69c5..61513fc 100644 --- a/packages/react-bootstrap-table2/src/sort/wrapper.js +++ b/packages/react-bootstrap-table2/src/sort/wrapper.js @@ -30,9 +30,7 @@ class SortWrapper extends Component { const { store } = this.props; store.sortBy(column); - this.table.setState({ - data: store.get() - }); + this.table.setState({ data: store.data }); } render() { @@ -40,7 +38,7 @@ class SortWrapper extends Component { ...this.props, ref: node => this.table = node, onSort: this.handleSort, - data: this.props.store.get() + data: this.props.store.data }); } } diff --git a/packages/react-bootstrap-table2/src/store/base.js b/packages/react-bootstrap-table2/src/store/base.js deleted file mode 100644 index d3b6a30..0000000 --- a/packages/react-bootstrap-table2/src/store/base.js +++ /dev/null @@ -1,110 +0,0 @@ -/* eslint class-methods-use-this: 0 */ -import { sort } from './sort'; -import Const from '../const'; -import _ from '../utils'; - -export default class Store { - constructor(props) { - const { data, keyField } = props; - this.keyField = keyField; - this.set(data); - - this.sortOrder = undefined; - this.sortField = undefined; - this.selected = []; - } - - isEmpty() { - return this.data.length === 0; - } - - sortBy({ dataField, sortFunc }, order) { - if (order) { - this.sortOrder = order; - } else if (dataField !== this.sortField) { - this.sortOrder = Const.SORT_DESC; - } else { - this.sortOrder = this.sortOrder === Const.SORT_DESC ? Const.SORT_ASC : Const.SORT_DESC; - } - - this.data = sort(dataField, this.data, this.sortOrder, sortFunc); - this.sortField = dataField; - } - - edit(rowId, dataField, newValue) { - const row = this.getRowByRowId(rowId); - if (row) _.set(row, dataField, newValue); - } - - get() { - return this.data; - } - - getByCurrPage(page, sizePerPage, pageStartIndex) { - const getNormalizedPage = () => { - const offset = Math.abs(1 - pageStartIndex); - return page + offset; - }; - const end = (getNormalizedPage() * sizePerPage) - 1; - const start = end - (sizePerPage - 1); - const dataSize = this.getDataSize(); - - const result = []; - for (let i = start; i <= end; i += 1) { - result.push(this.data[i]); - if (i + 1 === dataSize) break; - } - return result; - } - - set(data) { - this.data = data ? JSON.parse(JSON.stringify(data)) : []; - } - - getDataSize() { - return this.data.length; - } - - getRowByRowId(rowId) { - return this.get().find(row => _.get(row, this.keyField) === rowId); - } - - setSelectedRowKeys(selectedKeys) { - this.selected = selectedKeys; - } - - getSelectedRows() { - return this.selected.map(k => this.getRowByRowId(k)); - } - - getSelectedRowKeys() { - return this.selected; - } - - selectAllRows(nonSelectableRows = []) { - if (nonSelectableRows.length === 0) { - return this.data.map(row => _.get(row, this.keyField)); - } - return this.data - .filter(row => !nonSelectableRows.includes(_.get(row, this.keyField))) - .map(row => _.get(row, this.keyField)); - } - - cleanSelectedRows(nonSelectableRows = []) { - if (nonSelectableRows.length === 0) { - return []; - } - return this.selected.filter(x => nonSelectableRows.includes(x)); - } - - isAllRowsSelected() { - return this.data.length === this.selected.length; - } - - isAnySelectedRow(nonSelectableRows = []) { - if (nonSelectableRows.length === 0) { - return this.selected.length > 0; - } - return this.selected.filter(x => !nonSelectableRows.includes(x)).length; - } -} diff --git a/packages/react-bootstrap-table2/src/store/index.js b/packages/react-bootstrap-table2/src/store/index.js new file mode 100644 index 0000000..c39e47d --- /dev/null +++ b/packages/react-bootstrap-table2/src/store/index.js @@ -0,0 +1,41 @@ +/* eslint no-underscore-dangle: 0 */ +import _ from '../utils'; +import { sort, nextOrder } from './sort'; +import { getRowByRowId } from './rows'; + +export default class Store { + constructor(keyField) { + this._data = []; + this._keyField = keyField; + + this._sortOrder = undefined; + this._sortField = undefined; + this._selected = []; + } + + edit(rowId, dataField, newValue) { + const row = getRowByRowId(this)(rowId); + if (row) _.set(row, dataField, newValue); + } + + sortBy({ dataField, sortFunc }, order) { + this.sortOrder = nextOrder(this)(dataField, order); + this.sortField = dataField; + this.data = sort(this)(sortFunc); + } + + get data() { return this._data; } + set data(data) { this._data = (data ? JSON.parse(JSON.stringify(data)) : []); } + + get keyField() { return this._keyField; } + set keyField(keyField) { this._keyField = keyField; } + + get sortOrder() { return this._sortOrder; } + set sortOrder(sortOrder) { this._sortOrder = sortOrder; } + + get sortField() { return this._sortField; } + set sortField(sortField) { this._sortField = sortField; } + + get selected() { return this._selected; } + set selected(selected) { this._selected = selected; } +} diff --git a/packages/react-bootstrap-table2/src/store/rows.js b/packages/react-bootstrap-table2/src/store/rows.js new file mode 100644 index 0000000..4115122 --- /dev/null +++ b/packages/react-bootstrap-table2/src/store/rows.js @@ -0,0 +1,4 @@ + +export const matchRow = (keyField, id) => row => row[keyField] === id; + +export const getRowByRowId = ({ data, keyField }) => id => data.find(matchRow(keyField, id)); diff --git a/packages/react-bootstrap-table2/src/store/selection.js b/packages/react-bootstrap-table2/src/store/selection.js new file mode 100644 index 0000000..4c6c10a --- /dev/null +++ b/packages/react-bootstrap-table2/src/store/selection.js @@ -0,0 +1,33 @@ +import _ from '../utils'; +import { getRowByRowId } from './rows'; + +export const isSelectedAll = ({ data, selected }) => data.length === selected.length; + +export const isAnySelectedRow = ({ selected }) => (skips = []) => { + if (skips.length === 0) { + return selected.length > 0; + } + return selected.filter(x => !skips.includes(x)).length; +}; + +export const selectableKeys = ({ data, keyField }) => (skips = []) => { + if (skips.length === 0) { + return data.map(row => _.get(row, keyField)); + } + return data + .filter(row => !skips.includes(_.get(row, keyField))) + .map(row => _.get(row, keyField)); +}; + +export const unSelectableKeys = ({ selected }) => (skips = []) => { + if (skips.length === 0) { + return []; + } + return selected.filter(x => skips.includes(x)); +}; + +export const getSelectedRows = (store) => { + const getRow = getRowByRowId(store); + return store.selected.map(k => getRow(k)); +}; + diff --git a/packages/react-bootstrap-table2/src/store/sort.js b/packages/react-bootstrap-table2/src/store/sort.js index 95271b3..a2228bb 100644 --- a/packages/react-bootstrap-table2/src/store/sort.js +++ b/packages/react-bootstrap-table2/src/store/sort.js @@ -14,19 +14,19 @@ function comparator(a, b) { return result; } -const sort = (dataField, data, order, sortFunc) => { +export const sort = ({ data, sortOrder, sortField }) => (sortFunc) => { const _data = [...data]; _data.sort((a, b) => { let result; - let valueA = _.get(a, dataField); - let valueB = _.get(b, dataField); + let valueA = _.get(a, sortField); + let valueB = _.get(b, sortField); valueA = _.isDefined(valueA) ? valueA : ''; valueB = _.isDefined(valueB) ? valueB : ''; if (sortFunc) { - result = sortFunc(valueA, valueB, order, dataField); + result = sortFunc(valueA, valueB, sortOrder, sortField); } else { - if (order === Const.SORT_DESC) { + if (sortOrder === Const.SORT_DESC) { result = comparator(valueA, valueB); } else { result = comparator(valueB, valueA); @@ -37,4 +37,11 @@ const sort = (dataField, data, order, sortFunc) => { return _data; }; -export { sort }; +export const nextOrder = store => (field, order) => { + if (order) return order; + + if (field !== store.sortField) { + return Const.SORT_DESC; + } + return store.sortOrder === Const.SORT_DESC ? Const.SORT_ASC : Const.SORT_DESC; +}; diff --git a/packages/react-bootstrap-table2/test/bootstrap-table.test.js b/packages/react-bootstrap-table2/test/bootstrap-table.test.js index 2279420..458f456 100644 --- a/packages/react-bootstrap-table2/test/bootstrap-table.test.js +++ b/packages/react-bootstrap-table2/test/bootstrap-table.test.js @@ -3,7 +3,7 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import Caption from '../src/caption'; -import Store from '../src/store/base'; +import Store from '../src/store'; import Header from '../src/header'; import Body from '../src/body'; import BootstrapTable from '../src/bootstrap-table'; @@ -27,7 +27,8 @@ describe('BootstrapTable', () => { name: 'B' }]; - const store = new Store({ data }); + const store = new Store('id'); + store.data = data; describe('simplest table', () => { beforeEach(() => { @@ -44,7 +45,7 @@ describe('BootstrapTable', () => { it('should have correct default state', () => { expect(wrapper.state().data).toBeDefined(); - expect(wrapper.state().data).toEqual(store.get()); + expect(wrapper.state().data).toEqual(store.data); }); it('should have table-bordered class as default', () => { diff --git a/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js b/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js index c41d336..c8065b6 100644 --- a/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js +++ b/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js @@ -2,7 +2,7 @@ import React from 'react'; import sinon from 'sinon'; import { shallow } from 'enzyme'; -import Store from '../../src/store/base'; +import Store from '../../src/store'; import BootstrapTable from '../../src/bootstrap-table'; import CellEditWrapper from '../../src/cell-edit/wrapper'; @@ -31,7 +31,8 @@ describe('CellEditWrapper', () => { const keyField = 'id'; - const store = new Store({ data, keyField }); + const store = new Store(keyField); + store.data = data; beforeEach(() => { wrapper = shallow( diff --git a/packages/react-bootstrap-table2/test/container.test.js b/packages/react-bootstrap-table2/test/container.test.js index 4bb2f3c..b329403 100644 --- a/packages/react-bootstrap-table2/test/container.test.js +++ b/packages/react-bootstrap-table2/test/container.test.js @@ -6,6 +6,7 @@ import BootstrapTable from '../src'; import SortWrapper from '../src/sort/wrapper'; import CellEditWrapper from '../src/cell-edit/wrapper'; import RowSelectionWrapper from '../src/row-selection/wrapper'; +import { getRowByRowId } from '../src/store/rows'; describe('withDataStore', () => { let wrapper; @@ -42,7 +43,7 @@ describe('withDataStore', () => { it('should creating store successfully', () => { const store = wrapper.instance().store; expect(store).toBeDefined(); - expect(store.get()).toEqual(data); + expect(store.data).toEqual(data); expect(store.keyField).toEqual(keyField); }); }); @@ -90,7 +91,7 @@ describe('withDataStore', () => { it('should update store data directly', () => { const store = wrapper.instance().store; - const row = store.getRowByRowId(rowId); + const row = getRowByRowId(store)(rowId); expect(row[dataField]).toEqual(newValue); }); }); @@ -120,7 +121,7 @@ describe('withDataStore', () => { it('shouldn\'t update store data', () => { const store = wrapper.instance().store; - const row = store.getRowByRowId(rowId); + const row = getRowByRowId(store)(rowId); expect(row[dataField]).not.toEqual(newValue); }); }); diff --git a/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js b/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js index ed5c858..84b2c77 100644 --- a/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js +++ b/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js @@ -2,7 +2,7 @@ import React from 'react'; import sinon from 'sinon'; import { shallow } from 'enzyme'; -import Store from '../../src/store/base'; +import Store from '../../src/store'; import BootstrapTable from '../../src/bootstrap-table'; import RowSelectionWrapper from '../../src/row-selection/wrapper'; @@ -33,7 +33,8 @@ describe('RowSelectionWrapper', () => { const keyField = 'id'; - const store = new Store({ data, keyField }); + const store = new Store(keyField); + store.data = data; beforeEach(() => { wrapper = shallow( diff --git a/packages/react-bootstrap-table2/test/sort/wrapper.test.js b/packages/react-bootstrap-table2/test/sort/wrapper.test.js index 6dafc1b..6618192 100644 --- a/packages/react-bootstrap-table2/test/sort/wrapper.test.js +++ b/packages/react-bootstrap-table2/test/sort/wrapper.test.js @@ -3,7 +3,7 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; import Const from '../../src/const'; -import Store from '../../src/store/base'; +import Store from '../../src/store'; import BootstrapTable from '../../src/bootstrap-table'; import SortWrapper from '../../src/sort/wrapper'; @@ -30,7 +30,8 @@ describe('SortWrapper', () => { const keyField = 'id'; - let store = new Store({ data, keyField }); + let store = new Store(keyField); + store.data = data; beforeEach(() => { wrapper = shallow( @@ -56,7 +57,8 @@ describe('SortWrapper', () => { const sortColumn = columns[0]; beforeEach(() => { - store = new Store({ data, keyField }); + store = new Store(keyField); + store.data = data; wrapper = mount( { }); it('should render table with correct default sorted', () => { - expect(wrapper.props().data).toEqual(store.get()); + expect(wrapper.props().data).toEqual(store.data); }); it('should update store.sortField correctly', () => { diff --git a/packages/react-bootstrap-table2/test/store/base.test.js b/packages/react-bootstrap-table2/test/store/base.test.js deleted file mode 100644 index 28dea64..0000000 --- a/packages/react-bootstrap-table2/test/store/base.test.js +++ /dev/null @@ -1,245 +0,0 @@ -import Base from '../../src/store/base'; -import Const from '../../src/const'; -import _ from '../../src/utils'; - -describe('Store Base', () => { - let store; - let data; - - beforeEach(() => { - data = [ - { id: 3, name: 'name2' }, - { id: 2, name: 'ABC' }, - { id: 4, name: '123tester' }, - { id: 1, name: '!@#' } - ]; - store = new Base({ data, keyField: 'id' }); - }); - - describe('initialize', () => { - it('should have correct initialize data', () => { - expect(store.sortOrder).toBeUndefined(); - expect(store.sortField).toBeUndefined(); - expect(store.data.length).toEqual(data.length); - }); - }); - - describe('isEmpty', () => { - beforeEach(() => { - store = new Base({ data: [] }); - }); - - it('should have correct initialize data', () => { - expect(store.isEmpty()).toBeTruthy(); - }); - }); - - describe('sortBy', () => { - let dataField; - - beforeEach(() => { - dataField = 'name'; - }); - - it('should change sortField by dataField param', () => { - store.sortBy({ dataField }); - expect(store.sortField).toEqual(dataField); - }); - - it('should change sortOrder correctly when sortBy same dataField', () => { - store.sortBy({ dataField }); - expect(store.sortOrder).toEqual(Const.SORT_DESC); - store.sortBy({ dataField }); - expect(store.sortOrder).toEqual(Const.SORT_ASC); - }); - - it('should change sortOrder correctly when sortBy different dataField', () => { - store.sortBy({ dataField }); - expect(store.sortOrder).toEqual(Const.SORT_DESC); - - dataField = 'id'; - store.sortBy({ dataField }); - expect(store.sortOrder).toEqual(Const.SORT_DESC); - - dataField = 'name'; - store.sortBy({ dataField }); - expect(store.sortOrder).toEqual(Const.SORT_DESC); - }); - - it('should have correct result after sortBy', () => { - store.sortBy({ dataField }); - const result = store.data.map(e => e[dataField]).sort((a, b) => b - a); - store.get().forEach((e, i) => { - expect(e[dataField]).toEqual(result[i]); - }); - }); - - it('should force assign sortOrder correctly if second argument is passed', () => { - store.sortBy({ dataField }, Const.SORT_DESC); - expect(store.sortOrder).toEqual(Const.SORT_DESC); - }); - }); - - describe('getRowByRowId', () => { - it('should return data if specified id existing', () => { - const result = store.getRowByRowId(3); - expect(result).toBeDefined(); - }); - - it('should return undefined if specified id existing', () => { - const result = store.getRowByRowId(88); - expect(result).toBeUndefined(); - }); - }); - - describe('getSelectedRows', () => { - const selected = [1, 4]; - beforeEach(() => { - store.setSelectedRowKeys(selected); - }); - - it('should return all selected rows by store.selected', () => { - const result = store.getSelectedRows(); - expect(result).toBeDefined(); - expect(result.length).toEqual(2); - result.forEach((r) => { - expect(r).toBeDefined(); - expect(selected.includes(r[store.keyField])).toBeTruthy(); - }); - }); - }); - - describe('setSelectedRowKeys', () => { - const selected = [1, 4]; - it('should set store.selected correctly', () => { - store.setSelectedRowKeys(selected); - expect(store.getSelectedRowKeys()).toEqual(selected); - }); - }); - - describe('edit', () => { - it('should update a specified field correctly', () => { - const newValue = 'newValue'; - const dataField = 'name'; - const rowId = 2; - store.edit(rowId, dataField, newValue); - - const row = store.data.find(d => d[store.keyField] === rowId); - expect(row[dataField]).toEqual(newValue); - }); - - it('should not throw any error even if rowId is not existing', () => { - expect(() => { - store.edit('123', 'name', 'value'); - }).not.toThrow(); - }); - - it('should throwing error if dataField is not existing', () => { - expect(() => { - store.edit(2, 'non_exist_field', 'value'); - }).toThrow(); - }); - }); - - describe('selectAllRows', () => { - it('should return all row keys', () => { - const rowKeys = store.selectAllRows(); - - expect(Array.isArray(rowKeys)).toBeTruthy(); - expect(rowKeys).toEqual(data.map(x => x[store.keyField])); - }); - - it('should return correct row keys when nonSelectableRows args is not empty', () => { - const nonSelectableRows = [1, 3]; - const rowKeys = store.selectAllRows(nonSelectableRows); - - expect(Array.isArray(rowKeys)).toBeTruthy(); - expect(rowKeys).toEqual( - data - .filter(x => !nonSelectableRows.includes(_.get(x, store.keyField))) - .map(x => x[store.keyField]) - ); - }); - }); - - describe('isAllRowsSelected', () => { - it('should return true when all rows is selected', () => { - store.selected = data.map(row => _.get(row, store.keyField)); - - expect(store.isAllRowsSelected()).toBeTruthy(); - }); - - it('should return false when not all of rows is selected', () => { - store.selected = [1]; - - expect(store.isAllRowsSelected()).toBeFalsy(); - }); - }); - - describe('isAnySelectedRow', () => { - describe('if store.selected not empty', () => { - it('should return true', () => { - store.selected = data.map(row => _.get(row, store.keyField)); - expect(store.isAnySelectedRow()).toBeTruthy(); - }); - - describe('when nonSelectableRows given and not all of nonselectable rows are match current store.selected', () => { - const nonSelectableRows = [1, 3]; - beforeEach(() => { - store.selected = [1, 4]; - }); - - it('should return true', () => { - expect(store.isAnySelectedRow(nonSelectableRows)).toBeTruthy(); - }); - }); - - describe('when nonSelectableRows given and all of nonselectable rows are match current store.selected', () => { - const nonSelectableRows = [1, 3]; - beforeEach(() => { - store.selected = nonSelectableRows; - }); - - it('should return false', () => { - expect(store.isAnySelectedRow(nonSelectableRows)).toBeFalsy(); - }); - }); - }); - - it('should return false if store.selected is empty', () => { - store.selected = []; - - expect(store.isAnySelectedRow()).not.toBeTruthy(); - }); - }); - - describe('getByCurrPage', () => { - beforeEach(() => { - data = []; - for (let i = 0; i < 100; i += 1) { - data.push({ id: i, name: `test_name${i}` }); - } - store = new Base({ data, keyField: 'id' }); - }); - - it('should always return correct data', () => { - [ - // [page, sizePerPage, pageStartIndex] - [1, 10, 1], - [1, 25, 1], - [1, 30, 1], - [3, 30, 1], - [4, 30, 1], - [10, 10, 1], - [0, 10, 0], - [1, 10, 0], - [9, 10, 0] - ].forEach(([page, sizePerPage, pageStartIndex]) => { - const rows = store.getByCurrPage(page, sizePerPage, pageStartIndex); - expect(rows).toBeDefined(); - expect(Array.isArray(rows)).toBeTruthy(); - expect(rows.every(row => !!row)).toBeTruthy(); - }); - }); - }); -}); diff --git a/packages/react-bootstrap-table2/test/store/index.test.js b/packages/react-bootstrap-table2/test/store/index.test.js new file mode 100644 index 0000000..f6f9901 --- /dev/null +++ b/packages/react-bootstrap-table2/test/store/index.test.js @@ -0,0 +1,96 @@ +import Store from '../../src/store'; +import Const from '../../src/const'; + +describe('Store Base', () => { + let store; + let data; + + beforeEach(() => { + data = [ + { id: 3, name: 'name2' }, + { id: 2, name: 'ABC' }, + { id: 4, name: '123tester' }, + { id: 1, name: '!@#' } + ]; + store = new Store('id'); + store.data = data; + }); + + describe('initialize', () => { + it('should have correct initialize data', () => { + expect(store.sortOrder).toBeUndefined(); + expect(store.sortField).toBeUndefined(); + expect(store.data.length).toEqual(data.length); + }); + }); + + describe('sortBy', () => { + let dataField; + + beforeEach(() => { + dataField = 'name'; + }); + + it('should change sortField by dataField param', () => { + store.sortBy({ dataField }); + expect(store.sortField).toEqual(dataField); + }); + + it('should change sortOrder correctly when sortBy same dataField', () => { + store.sortBy({ dataField }); + expect(store.sortOrder).toEqual(Const.SORT_DESC); + store.sortBy({ dataField }); + expect(store.sortOrder).toEqual(Const.SORT_ASC); + }); + + it('should change sortOrder correctly when sortBy different dataField', () => { + store.sortBy({ dataField }); + expect(store.sortOrder).toEqual(Const.SORT_DESC); + + dataField = 'id'; + store.sortBy({ dataField }); + expect(store.sortOrder).toEqual(Const.SORT_DESC); + + dataField = 'name'; + store.sortBy({ dataField }); + expect(store.sortOrder).toEqual(Const.SORT_DESC); + }); + + it('should have correct result after sortBy', () => { + store.sortBy({ dataField }); + const result = store.data.map(e => e[dataField]).sort((a, b) => b - a); + store.data.forEach((e, i) => { + expect(e[dataField]).toEqual(result[i]); + }); + }); + + it('should force assign sortOrder correctly if second argument is passed', () => { + store.sortBy({ dataField }, Const.SORT_DESC); + expect(store.sortOrder).toEqual(Const.SORT_DESC); + }); + }); + + describe('edit', () => { + it('should update a specified field correctly', () => { + const newValue = 'newValue'; + const dataField = 'name'; + const rowId = 2; + store.edit(rowId, dataField, newValue); + + const row = store.data.find(d => d[store.keyField] === rowId); + expect(row[dataField]).toEqual(newValue); + }); + + it('should not throw any error even if rowId is not existing', () => { + expect(() => { + store.edit('123', 'name', 'value'); + }).not.toThrow(); + }); + + it('should throwing error if dataField is not existing', () => { + expect(() => { + store.edit(2, 'non_exist_field', 'value'); + }).toThrow(); + }); + }); +}); diff --git a/packages/react-bootstrap-table2/test/store/rows.test.js b/packages/react-bootstrap-table2/test/store/rows.test.js new file mode 100644 index 0000000..e8d1731 --- /dev/null +++ b/packages/react-bootstrap-table2/test/store/rows.test.js @@ -0,0 +1,30 @@ +import Store from '../../src/store'; +import { getRowByRowId } from '../../src/store/rows'; + +describe('Rows Function', () => { + const data = [ + { id: 3, name: 'name2' }, + { id: 2, name: 'ABC' }, + { id: 4, name: '123tester' }, + { id: 1, name: '!@#' } + ]; + const keyField = 'id'; + let store; + let fn; + + beforeEach(() => { + store = new Store(keyField); + store.data = data; + fn = getRowByRowId(store); + }); + + describe('getRowByRowId', () => { + it('should returning correct row', () => { + expect(fn(2)).toEqual(data[1]); + }); + + it('should returning undefined if not existing', () => { + expect(fn(20)).not.toBeDefined(); + }); + }); +}); diff --git a/packages/react-bootstrap-table2/test/store/selection.test.js b/packages/react-bootstrap-table2/test/store/selection.test.js new file mode 100644 index 0000000..6d453c7 --- /dev/null +++ b/packages/react-bootstrap-table2/test/store/selection.test.js @@ -0,0 +1,104 @@ +import Store from '../../src/store'; +import { + isSelectedAll, + isAnySelectedRow, + selectableKeys, + unSelectableKeys, + getSelectedRows +} from '../../src/store/selection'; + +describe('Selection Function', () => { + const data = [ + { id: 3, name: 'name2' }, + { id: 2, name: 'ABC' }, + { id: 4, name: '123tester' }, + { id: 1, name: '!@#' } + ]; + const keyField = 'id'; + let store; + let skip; + let fn; + + beforeEach(() => { + store = new Store(keyField); + store.data = data; + }); + + describe('isSelectedAll', () => { + it('should returning false when store.selected is not cover all rows', () => { + expect(isSelectedAll(store)).toBeFalsy(); + store.selected = [data[0][keyField]]; + expect(isSelectedAll(store)).toBeFalsy(); + }); + + it('should returning true when store.selected is cover all rows', () => { + store.selected = data.map(d => d[keyField]); + expect(isSelectedAll(store)).toBeTruthy(); + }); + }); + + describe('isAnySelectedRow', () => { + it('should returning false if any store.selected is empty', () => { + fn = isAnySelectedRow(store); + expect(fn()).toBeFalsy(); + }); + + it('should returning false if store.selected is have same key as skips', () => { + fn = isAnySelectedRow(store); + skip = [data[0][keyField]]; + store.selected = [data[0][keyField]]; + expect(fn(skip)).toBeFalsy(); + }); + + it('should returning true if store.selected is not empty', () => { + store.selected = [data[0][keyField]]; + fn = isAnySelectedRow(store); + expect(fn()).toBeTruthy(); + }); + + it('should returning true if length of store.selected is bigger than skips', () => { + store.selected = [data[0][keyField], data[2][keyField]]; + skip = [data[0][keyField]]; + fn = isAnySelectedRow(store); + expect(fn(skip)).toBeTruthy(); + }); + }); + + describe('selectableKeys', () => { + beforeEach(() => { + fn = selectableKeys(store); + }); + + it('should returning all row keys if skip is empty', () => { + expect(fn()).toEqual(data.map(d => d[keyField])); + }); + + it('should returngin row keys expect the skip', () => { + skip = [data[1][keyField]]; + expect(fn(skip)).toHaveLength(data.length - skip.length); + }); + }); + + describe('unSelectableKeys', () => { + it('should returning empty array if skip is empty', () => { + fn = unSelectableKeys(store); + expect(fn()).toHaveLength(0); + }); + + it('should returning array which must contain skip', () => { + skip = [data[1][keyField]]; + store.selected = data.map(d => d[keyField]); + fn = unSelectableKeys(store); + expect(fn(skip)).toHaveLength(skip.length); + }); + }); + + describe('getSelectedRows', () => { + it('should returning rows object correctly', () => { + store.selected = data.map(d => d[keyField]); + const result = getSelectedRows(store); + expect(result).toHaveLength(store.selected.length); + expect(result).toEqual(store.data); + }); + }); +}); diff --git a/packages/react-bootstrap-table2/test/store/sort.test.js b/packages/react-bootstrap-table2/test/store/sort.test.js index 1eb42ff..3279cd5 100644 --- a/packages/react-bootstrap-table2/test/store/sort.test.js +++ b/packages/react-bootstrap-table2/test/store/sort.test.js @@ -1,6 +1,7 @@ import sinon from 'sinon'; -import { sort } from '../../src/store/sort'; +import Store from '../../src/store'; +import { sort, nextOrder } from '../../src/store/sort'; import Const from '../../src/const'; describe('Sort Function', () => { @@ -11,29 +12,65 @@ describe('Sort Function', () => { { id: 1, name: '!@#' } ]; - it('should sort array with ASC order correctly', () => { - const result = sort('id', data, Const.SORT_ASC); - expect(result.length).toEqual(data.length); + let store; - const sortedArray = data.map(e => e.id).sort((a, b) => a - b); - sortedArray.forEach((e, i) => { - expect(e).toEqual(result[i].id); + describe('sort', () => { + beforeEach(() => { + store = new Store('id'); + store.data = data; + }); + + it('should sort array with ASC order correctly', () => { + store.sortField = 'id'; + store.sortOrder = Const.SORT_ASC; + const result = sort(store)(); + expect(result.length).toEqual(data.length); + + const sortedArray = data.map(e => e.id).sort((a, b) => a - b); + sortedArray.forEach((e, i) => { + expect(e).toEqual(result[i].id); + }); + }); + + it('should sort array with DESC order correctly', () => { + store.sortField = 'id'; + store.sortOrder = Const.SORT_DESC; + const result = sort(store)(); + expect(result.length).toEqual(data.length); + + const sortedArray = data.map(e => e.id).sort((a, b) => b - a); + sortedArray.forEach((e, i) => { + expect(e).toEqual(result[i].id); + }); + }); + + it('should call custom sort function when sortFunc given', () => { + const sortFunc = sinon.stub().returns(1); + store.sortField = 'id'; + store.sortOrder = Const.SORT_DESC; + sort(store)(sortFunc); + expect(sortFunc.callCount).toBe(6); }); }); - it('should sort array with DESC order correctly', () => { - const result = sort('id', data, Const.SORT_DESC); - expect(result.length).toEqual(data.length); - - const sortedArray = data.map(e => e.id).sort((a, b) => b - a); - sortedArray.forEach((e, i) => { - expect(e).toEqual(result[i].id); + describe('nextOrder', () => { + beforeEach(() => { + store = new Store('id'); + store.data = data; }); - }); - it('should call custom sort function when sortFunc given', () => { - const sortFunc = sinon.stub().returns(1); - sort('id', data, Const.SORT_DESC, sortFunc); - expect(sortFunc.callCount).toBe(6); + it('should return correcly order when store.sortField is not eq next sort field', () => { + expect(nextOrder(store)('name')).toBe(Const.SORT_DESC); + }); + + it('should return correcly order when store.sortField is eq next sort field', () => { + store.sortField = 'name'; + store.sortOrder = Const.SORT_DESC; + expect(nextOrder(store)('name')).toBe(Const.SORT_ASC); + }); + + it('should return correcly order when order is specified', () => { + expect(nextOrder(store)('name', Const.SORT_ASC)).toBe(Const.SORT_ASC); + }); }); }); diff --git a/packages/react-bootstrap-table2/test/test-helpers/mock-component.js b/packages/react-bootstrap-table2/test/test-helpers/mock-component.js index f09890e..c3282a4 100644 --- a/packages/react-bootstrap-table2/test/test-helpers/mock-component.js +++ b/packages/react-bootstrap-table2/test/test-helpers/mock-component.js @@ -1,4 +1,4 @@ -import Store from '../../src/store/base'; +import Store from '../../src/store'; export const extendTo = Base => class MockComponent extends Base { @@ -7,7 +7,8 @@ export const extendTo = Base => const { data } = props; - this.store = new Store(props); + this.store = new Store(props.keyField); + this.store.data = data; this.state = { data }; }