diff --git a/app/javascript/packs/shared/data_grid/DataGrid.jsx b/app/javascript/packs/shared/data_grid/DataGrid.jsx index 3f46438ad..00f6c6045 100644 --- a/app/javascript/packs/shared/data_grid/DataGrid.jsx +++ b/app/javascript/packs/shared/data_grid/DataGrid.jsx @@ -11,6 +11,53 @@ class DataGrid extends Component { this.transformColumns(); this.setupDefaultProps(); + + // Store the original rows array, and make a copy that + // can be used for modifying eg.filtering, sorting + this.state = { + originalRows: this.props.data, + rows: this.props.data.slice(0) + }; + } + + setupDefaultProps() { + // Setup the default props if they're not provided + if ('rowGetter' in this.props) { + this._rowGetter = this.props.rowGetter; + } else { + this._rowGetter = ((i) => this.state.rows[i]); + } + this._rowGetter = this._rowGetter.bind(this); + + if ('rowsCount' in this.props) { + this._rowsCount = this.props.rowsCount; + } else { + this._rowsCount = this.props.data.length; + } + + if ('onGridSort' in this.props) { + this._onGridSort = this.props.onGridSort; + } else { + this._onGridSort = ((sortColumn, sortDirection) => { + const comparer = (a, b) => { + if (sortDirection === 'ASC') { + return (a[sortColumn] > b[sortColumn]) ? 1 : -1; + } else if (sortDirection === 'DESC') { + return (a[sortColumn] < b[sortColumn]) ? 1 : -1; + } + return 0; + }; + let rows; + if (sortDirection === 'NONE') { + rows = this.state.originalRows.slice(0); + } else { + rows = this.state.rows.sort(comparer); + } + + this.setState({ rows }); + }); + } + this._onGridSort = this._onGridSort.bind(this); } transformColumns() { @@ -18,41 +65,21 @@ class DataGrid extends Component { // ReactDataGrid-compatible representation this._columns = this.props.columns - .sort((a, b) => b.position - a.position) - .filter((col) => col.visible) - .map((col) => { - return { - key: col.textId, - name: col.name, - locked: col.locked - }; - }); - } - - setupDefaultProps() { - // Setup the default props if they're not provided - const self = this; - - if ('rowGetter' in this.props) { - this._rowGetter = this.props.rowGetter; - } else { - this._rowGetter = function(i) { - return this.props.data[i]; - }.bind(this); - } - if ('rowsCount' in this.props) { - this._rowsCount = this.props.rowsCount; - } else { - this._rowsCount = this.props.data.length; - } + .sort((a, b) => a.position - b.position) + .filter((col) => (!('visible' in col) || col.visible)) + .map((col) => ({ + key: col.textId, + name: col.name, + locked: col.locked, + sortable: col.sortable + })); } cleanProps() { // Remove additional props from the props value - const cleanProps = {...this.props}; - delete cleanProps.columns; - delete cleanProps.rowGetter; - delete cleanProps.rowsCount; + const { + columns, rowGetter, rowsCount, ...cleanProps + } = this.props; return cleanProps; } @@ -62,6 +89,7 @@ class DataGrid extends Component { columns={this._columns} rowGetter={this._rowGetter} rowsCount={this._rowsCount} + onGridSort={this._onGridSort} {...this.cleanProps()} /> ); @@ -69,6 +97,22 @@ class DataGrid extends Component { } DataGrid.propTypes = { + columns: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + isKey: PropTypes.bool.isRequired, + textId: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + position: PropTypes.number.isRequired, + visible: PropTypes.bool, + sortable: PropTypes.bool, + locked: PropTypes.bool + }) + ).isRequired, + data: PropTypes.arrayOf(PropTypes.object).isRequired, + rowGetter: PropTypes.func, + rowsCount: PropTypes.number, + onGridSort: PropTypes.func }; export default DataGrid; \ No newline at end of file diff --git a/app/javascript/packs/shared/data_table/DataTable.jsx b/app/javascript/packs/shared/data_table/DataTable.jsx index a313ca56d..58ddf0d53 100644 --- a/app/javascript/packs/shared/data_table/DataTable.jsx +++ b/app/javascript/packs/shared/data_table/DataTable.jsx @@ -3,6 +3,15 @@ import PropTypes from "prop-types"; import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; class DataTable extends Component { + static cleanColumnAttributes(col) { + // Remove additional attributes from the columns + const { + id, isKey, textId, name, position, visible, + sortable, locked, ...cleanCol + } = col; + return cleanCol; + } + constructor(props) { super(props); this.cleanProps = this.cleanProps.bind(this); @@ -11,25 +20,25 @@ class DataTable extends Component { cleanProps() { // Remove additional props from the props value - const cleanProps = {...this.props}; - delete cleanProps.columns; + const {columns, ...cleanProps} = this.props; return cleanProps; } + displayHeader() { - const orderedCols = [...this.props.columns].sort((a, b) => b.position - a.position); - return orderedCols.map((col) => { - return ( - - ); - }); + const orderedCols = [...this.props.columns].sort((a, b) => a.position - b.position); + return orderedCols.map((col) => + + ); } render() { @@ -53,7 +62,8 @@ DataTable.propTypes = { sortable: PropTypes.bool, locked: PropTypes.bool }) - ).isRequired + ).isRequired, + data: PropTypes.arrayOf(PropTypes.object).isRequired }; export default DataTable; \ No newline at end of file diff --git a/app/javascript/packs/styles/main.scss b/app/javascript/packs/styles/main.scss index 772249944..6d077be06 100644 --- a/app/javascript/packs/styles/main.scss +++ b/app/javascript/packs/styles/main.scss @@ -7,3 +7,44 @@ body { font-family: "Open Sans",Arial,Helvetica,sans-serif; font-size: 13px; } + +.react-bs-table { + thead { + background-color: #909088; + + > tr > th, + >tr > td { + padding: 6px; + padding-right: 30px; + } + + > tr > th { + border-bottom: 2px solid #ddd; + border-bottom-width: 0; + border-left: 2px solid #fcfcfc; + color: #fff; + font-weight: normal; + vertical-align: bottom; + + &:first-child { + border-left: none; + } + } + } + + td, th { + box-sizing: content-box; + } + + td { + overflow-wrap: break-word; + text-overflow: ellipsis; + word-break: break-word; + } + + .sorting_desc, + .sorting_asc { + background-color: #37a0d9; + } +} +