mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2025-10-16 11:55:39 +00:00
Development/sorted classes and style (#136)
* fix missing defaultSorted props for default sort sample * implement customized classes for sorted header * [test] test for sorted header classes * implement customized style for sorted header * [test] test for sorted header style * update document * add missing props check and fix typo * seperate sorting style and header into two props * [test] add test case if column.headerStyle and column.headerClasses were defined * implement customized header style and classes in column level * [test] test for customized header style and classes in column level * [DOC] document for customized classes and styles * sample for customized classes and styles * typo fix for document * tuning the wording for test and documents
This commit is contained in:
parent
574a3146fc
commit
00f1105c8d
@ -25,6 +25,8 @@ Available properties in a column object:
|
||||
* [headerEvents](#headerEvents)
|
||||
* [headerAlign](#headerAlign)
|
||||
* [headerAttrs](#headerAttrs)
|
||||
* [headerSortingClasses](#headerSortingClasses)
|
||||
* [headerSortingStyle](#headerSortingStyle)
|
||||
* [editable](#editable)
|
||||
* [validator](#validator)
|
||||
* [editCellStyle](#editCellStyle)
|
||||
@ -419,6 +421,35 @@ A new `Object` will be the result of element headerAttrs.
|
||||
> Same as [column.attrs](#attrs), it has lower priority and will be
|
||||
> overwrited when other props related to HTML attributes were given.
|
||||
|
||||
### <a name='headerSortingClasses'>headerSortingClasses - [String | Function]</a>
|
||||
|
||||
`headerSortingClasses` allows to customize `class` for header cell when this column is sorting.
|
||||
|
||||
```js
|
||||
const headerSortingClasses = 'demo-sorting';
|
||||
```
|
||||
|
||||
Furthermore, it also accepts a callback which takes **4** arguments and `String` is expected to return:
|
||||
|
||||
```js
|
||||
const headerSortingClasses = (column, sortOrder, isLastSorting, colIndex) => { ... }
|
||||
```
|
||||
|
||||
* `column`: The value of current column.
|
||||
* `sortOrder`: The order of current sorting
|
||||
* `isLastSorting`: Is the last one of sorted columns.
|
||||
* `colIndex`: The index of the current column being processed in BootstrapTable.
|
||||
|
||||
### <a name='headerSortingStyle'>headerSortingStyle - [Object | Function]</a>
|
||||
|
||||
It's similiar to [headerSortingClasses](#headerSortingClasses). It allows to customize the style of header cell when this column is sorting. A style `Object` and `callback` are acceptable. `callback` takes **4** arguments and an `Object` is expected to return:
|
||||
|
||||
```js
|
||||
const sortingHeaderStyle = {
|
||||
backgroundColor: 'red'
|
||||
};
|
||||
```
|
||||
|
||||
## <a name='editable'>column.editable - [Bool | Function]</a>
|
||||
`column.editable` default is true, means every column is editable if you configure [`cellEdit`](./README.md#cellEdit). But you can disable some columns editable via setting `false`.
|
||||
|
||||
|
||||
@ -41,7 +41,17 @@ const columns = [{
|
||||
sort: true
|
||||
}];
|
||||
|
||||
<BootstrapTable keyField='id' data={ products } columns={ columns } />
|
||||
const defaultSorted = [{
|
||||
dataField: 'name',
|
||||
order: 'desc'
|
||||
}];
|
||||
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
defaultSorted={ defaultSorted }
|
||||
/>
|
||||
`;
|
||||
|
||||
|
||||
|
||||
61
packages/react-bootstrap-table2-example/examples/sort/header-sorting-classes.js
vendored
Normal file
61
packages/react-bootstrap-table2-example/examples/sort/header-sorting-classes.js
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/* eslint
|
||||
no-unused-vars: 0
|
||||
arrow-body-style: 0
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import BootstrapTable from 'react-bootstrap-table2';
|
||||
import Code from 'components/common/code-block';
|
||||
import { productsGenerator } from 'utils/common';
|
||||
|
||||
const products = productsGenerator();
|
||||
|
||||
const headerSortingClasses = (column, sortOrder, isLastSorting, colIndex) => (
|
||||
sortOrder === 'asc' ? 'demo-sorting-asc' : 'demo-sorting-desc'
|
||||
);
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID',
|
||||
sort: true,
|
||||
headerSortingClasses
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
sort: true,
|
||||
headerSortingClasses
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const sourceCode = `\
|
||||
const headerSortingClasses = (column, sortOrder, isLastSorting, colIndex) => (
|
||||
sortOrder === 'asc' ? 'demo-sorting-asc' : 'demo-sorting-desc'
|
||||
);
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID',
|
||||
sort: true,
|
||||
headerSortingClasses
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
sort: true,
|
||||
headerSortingClasses
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } />
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
54
packages/react-bootstrap-table2-example/examples/sort/header-sorting-style.js
vendored
Normal file
54
packages/react-bootstrap-table2-example/examples/sort/header-sorting-style.js
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
import React from 'react';
|
||||
|
||||
import BootstrapTable from 'react-bootstrap-table2';
|
||||
import Code from 'components/common/code-block';
|
||||
import { productsGenerator } from 'utils/common';
|
||||
|
||||
const products = productsGenerator();
|
||||
|
||||
const headerSortingStyle = { backgroundColor: '#c8e6c9' };
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID',
|
||||
sort: true,
|
||||
headerSortingStyle
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
sort: true,
|
||||
headerSortingStyle
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
|
||||
const sourceCode = `\
|
||||
const headerSortingStyle = { backgroundColor: '#c8e6c9' };
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID',
|
||||
sort: true,
|
||||
headerSortingStyle
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
sort: true,
|
||||
headerSortingStyle
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } />
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
@ -41,6 +41,8 @@ import RowEventTable from 'examples/rows/row-event';
|
||||
import EnableSortTable from 'examples/sort/enable-sort-table';
|
||||
import DefaultSortTable from 'examples/sort/default-sort-table';
|
||||
import CustomSortTable from 'examples/sort/custom-sort-table';
|
||||
import HeaderSortingClassesTable from 'examples/sort/header-sorting-classes';
|
||||
import HeaderSortingStyleTable from 'examples/sort/header-sorting-style';
|
||||
|
||||
// cell editing
|
||||
import ClickToEditTable from 'examples/cell-edit/click-to-edit-table';
|
||||
@ -127,7 +129,9 @@ storiesOf('Work on Rows', module)
|
||||
storiesOf('Sort Table', module)
|
||||
.add('Enable Sort', () => <EnableSortTable />)
|
||||
.add('Default Sort Table', () => <DefaultSortTable />)
|
||||
.add('Custom Sort Fuction', () => <CustomSortTable />);
|
||||
.add('Custom Sort Fuction', () => <CustomSortTable />)
|
||||
.add('Custom Classes on Sorting Header Column', () => <HeaderSortingClassesTable />)
|
||||
.add('Custom Style on Sorting Header Column', () => <HeaderSortingStyleTable />);
|
||||
|
||||
storiesOf('Cell Editing', module)
|
||||
.add('Click to Edit', () => <ClickToEditTable />)
|
||||
|
||||
@ -10,4 +10,5 @@ $grey-900: #212121;
|
||||
$pink-500: #E91E63;
|
||||
$green-lighten-2: #81c784;
|
||||
$green-lighten-4: #c8e6c9;
|
||||
$light-blue: #00BFFF;
|
||||
$markdown-color: #f6f8fa;
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
.demo-sorting,
|
||||
.demo-sorting-asc {
|
||||
background-color: $green-lighten-2;
|
||||
}
|
||||
|
||||
.demo-sorting-desc {
|
||||
background-color: $light-blue;
|
||||
}
|
||||
@ -8,4 +8,5 @@
|
||||
@import "cell-edit/index";
|
||||
@import "row-selection/index";
|
||||
@import "rows/index";
|
||||
@import "loading-overlay/index";
|
||||
@import "sort/index";
|
||||
@import "loading-overlay/index";
|
||||
|
||||
@ -15,8 +15,10 @@ const HeaderCell = (props) => {
|
||||
index,
|
||||
onSort,
|
||||
sorting,
|
||||
sortOrder
|
||||
sortOrder,
|
||||
isLastSorting
|
||||
} = props;
|
||||
|
||||
const {
|
||||
text,
|
||||
sort,
|
||||
@ -27,7 +29,9 @@ const HeaderCell = (props) => {
|
||||
headerEvents,
|
||||
headerClasses,
|
||||
headerStyle,
|
||||
headerAttrs
|
||||
headerAttrs,
|
||||
headerSortingClasses,
|
||||
headerSortingStyle
|
||||
} = column;
|
||||
|
||||
const cellAttrs = {
|
||||
@ -35,10 +39,10 @@ const HeaderCell = (props) => {
|
||||
...headerEvents
|
||||
};
|
||||
const children = headerFormatter ? headerFormatter(column, index) : text;
|
||||
const cellClasses = _.isFunction(headerClasses) ? headerClasses(column, index) : headerClasses;
|
||||
|
||||
let cellStyle = {};
|
||||
let sortSymbol;
|
||||
let cellClasses = _.isFunction(headerClasses) ? headerClasses(column, index) : headerClasses;
|
||||
|
||||
if (headerStyle) {
|
||||
cellStyle = _.isFunction(headerStyle) ? headerStyle(column, index) : headerStyle;
|
||||
@ -56,10 +60,6 @@ const HeaderCell = (props) => {
|
||||
cellStyle.display = 'none';
|
||||
}
|
||||
|
||||
if (cellClasses) cellAttrs.className = cellClasses;
|
||||
|
||||
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
|
||||
|
||||
if (sort) {
|
||||
const customClick = cellAttrs.onClick;
|
||||
cellAttrs.onClick = (e) => {
|
||||
@ -70,11 +70,30 @@ const HeaderCell = (props) => {
|
||||
|
||||
if (sorting) {
|
||||
sortSymbol = <SortCaret order={ sortOrder } />;
|
||||
|
||||
// append customized classes or style if table was sorting based on the current column.
|
||||
cellClasses = cs(
|
||||
cellClasses,
|
||||
_.isFunction(headerSortingClasses)
|
||||
? headerSortingClasses(column, sortOrder, isLastSorting, index)
|
||||
: headerSortingClasses
|
||||
);
|
||||
|
||||
cellStyle = {
|
||||
...cellStyle,
|
||||
..._.isFunction(headerSortingStyle)
|
||||
? headerSortingStyle(column, sortOrder, isLastSorting, index)
|
||||
: headerSortingStyle
|
||||
};
|
||||
} else {
|
||||
sortSymbol = <SortSymbol />;
|
||||
}
|
||||
}
|
||||
|
||||
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
|
||||
|
||||
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
|
||||
|
||||
return (
|
||||
<th { ...cellAttrs }>
|
||||
{ children }{ sortSymbol }
|
||||
@ -112,7 +131,8 @@ HeaderCell.propTypes = {
|
||||
index: PropTypes.number.isRequired,
|
||||
onSort: PropTypes.func,
|
||||
sorting: PropTypes.bool,
|
||||
sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC])
|
||||
sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]),
|
||||
isLastSorting: PropTypes.bool
|
||||
};
|
||||
|
||||
export default HeaderCell;
|
||||
|
||||
@ -27,6 +27,8 @@ const Header = (props) => {
|
||||
{
|
||||
columns.map((column, i) => {
|
||||
const currSort = column.dataField === sortField;
|
||||
const isLastSorting = column.dataField === sortField;
|
||||
|
||||
return (
|
||||
<HeaderCell
|
||||
index={ i }
|
||||
@ -35,6 +37,7 @@ const Header = (props) => {
|
||||
onSort={ onSort }
|
||||
sorting={ currSort }
|
||||
sortOrder={ sortOrder }
|
||||
isLastSorting={ isLastSorting }
|
||||
/>);
|
||||
})
|
||||
}
|
||||
|
||||
@ -436,6 +436,233 @@ describe('HeaderCell', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when headerSortingClasses is defined ', () => {
|
||||
const classes = 'foo';
|
||||
const order = Const.SORT_DESC;
|
||||
|
||||
describe('if headerSortingClasses is a string', () => {
|
||||
beforeEach(() => {
|
||||
column = { ...column, headerSortingClasses: classes };
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
/>);
|
||||
});
|
||||
|
||||
it('should append classes correctly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.hasClass(classes)).toBe(true);
|
||||
});
|
||||
|
||||
it('should have sortable class on header cell', () => {
|
||||
expect(wrapper.hasClass('sortable')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if headerSortingClasses is a function', () => {
|
||||
let classesCallBack;
|
||||
let isLastSorting;
|
||||
|
||||
beforeEach(() => {
|
||||
classesCallBack = sinon.stub()
|
||||
.withArgs(column, order, isLastSorting, index)
|
||||
.returns(classes);
|
||||
|
||||
column = { ...column, headerSortingClasses: classesCallBack };
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
/>);
|
||||
});
|
||||
|
||||
it('should append classes correctly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.hasClass(classes)).toBe(true);
|
||||
});
|
||||
|
||||
it('should have sortable class on header cell', () => {
|
||||
expect(wrapper.hasClass('sortable')).toBe(true);
|
||||
});
|
||||
|
||||
it('should call custom class function with correct params', () => {
|
||||
expect(classesCallBack.callCount).toBe(1);
|
||||
expect(classesCallBack.calledWith(column, order, isLastSorting, index)).toBe(true);
|
||||
});
|
||||
|
||||
describe('when the field is last sorting', () => {
|
||||
it('should call custom classes function with isLastSorting being true', () => {
|
||||
isLastSorting = true;
|
||||
classesCallBack.reset();
|
||||
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
isLastSorting
|
||||
/>);
|
||||
|
||||
expect(classesCallBack.callCount).toBe(1);
|
||||
expect(classesCallBack.calledWith(column, order, isLastSorting, index)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('if column.headerClasses is defined as well', () => {
|
||||
it('should keep both classes', () => {
|
||||
column = {
|
||||
...column,
|
||||
headerClasses: 'td-test-class',
|
||||
headerSortingClasses: classes
|
||||
};
|
||||
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.hasClass('sortable')).toBe(true);
|
||||
expect(wrapper.hasClass(classes)).toBe(true);
|
||||
expect(wrapper.hasClass(column.headerClasses)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when headerSortingStyle is defined', () => {
|
||||
const style = { backgroundColor: 'red' };
|
||||
const order = Const.SORT_DESC;
|
||||
|
||||
describe('if headerSortingStyle is an object', () => {
|
||||
beforeEach(() => {
|
||||
column = { ...column, headerSortingStyle: style };
|
||||
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should append style correctly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('th').prop('style')).toEqual(style);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if headerSortingStyle is a function', () => {
|
||||
let styleCallBack;
|
||||
let isLastSorting;
|
||||
|
||||
beforeEach(() => {
|
||||
styleCallBack = sinon.stub()
|
||||
.withArgs(column, order, isLastSorting, index)
|
||||
.returns(style);
|
||||
|
||||
column = { ...column, headerSortingStyle: styleCallBack };
|
||||
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
/>);
|
||||
});
|
||||
|
||||
it('should append style correctly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('th').prop('style')).toEqual(style);
|
||||
});
|
||||
|
||||
it('should call custom style function with correct params', () => {
|
||||
expect(styleCallBack.callCount).toBe(1);
|
||||
expect(styleCallBack.calledWith(column, order, isLastSorting, index)).toBe(true);
|
||||
});
|
||||
|
||||
describe('when the field is last sorting', () => {
|
||||
it('should call custom classes function with isLastSorting being true', () => {
|
||||
isLastSorting = true;
|
||||
styleCallBack.reset();
|
||||
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
isLastSorting
|
||||
/>);
|
||||
|
||||
expect(styleCallBack.callCount).toBe(1);
|
||||
expect(styleCallBack.calledWith(column, order, isLastSorting, index)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('if column.headerStyle was defined as well', () => {
|
||||
it('should keep both styles', () => {
|
||||
column = {
|
||||
...column,
|
||||
headerStyle: { opacity: '1' },
|
||||
headerSortingStyle: style
|
||||
};
|
||||
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
/>
|
||||
);
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('th').prop('style')).toEqual(expect.objectContaining({
|
||||
...style,
|
||||
...column.headerStyle
|
||||
}));
|
||||
});
|
||||
|
||||
it('headerSortingStyle should have higher priority', () => {
|
||||
column = {
|
||||
...column,
|
||||
headerStyle: { backgroundColor: 'green' },
|
||||
headerSortingStyle: style
|
||||
};
|
||||
|
||||
wrapper = shallow(
|
||||
<HeaderCell
|
||||
column={ column }
|
||||
index={ index }
|
||||
sorting
|
||||
sortOrder={ order }
|
||||
/>
|
||||
);
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('th').prop('style')).toEqual(expect.objectContaining({
|
||||
...style
|
||||
}));
|
||||
expect(wrapper.find('th').prop('style')).not.toEqual(expect.objectContaining({
|
||||
...column.headerStyle
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when column.headerEvents prop is defined and have custom onClick', () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user