Skip to content

Commit

Permalink
Merge pull request #440 from appfolio/wipUncontrolledTable
Browse files Browse the repository at this point in the history
gt - WIP UncontrolledTable
  • Loading branch information
gthomas-appfolio authored Aug 31, 2018
2 parents 818871c + 9c8fa3c commit 7e19f6f
Show file tree
Hide file tree
Showing 8 changed files with 684 additions and 322 deletions.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@
"enzyme-adapter-react-16": "^1.1.1",
"fecha": "^2.3.0",
"lodash.flow": "^3.5.0",
"lodash.includes": "^4.3.0",
"lodash.noop": "^3.0.1",
"lodash.range": "^3.2.0",
"lodash.orderby": "^4.6.0",
"lodash.without": "^4.4.0",
"prop-types": "^15.5.10",
"react-fontawesome": "^1.4.0",
"react-onclickoutside": "^6.7.1",
Expand Down
201 changes: 201 additions & 0 deletions src/components/UncontrolledTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import includes from 'lodash.includes';
import orderBy from 'lodash.orderby';
import without from 'lodash.without';
import Button from './Button';
import Icon from './Icon';
import Paginator from './Paginator';
import SortableTable from './SortableTable';

export default class UncontrolledTable extends React.Component {
static propTypes = {
...SortableTable.propTypes,
pageSize: PropTypes.number,
sort: PropTypes.shape({
column: PropTypes.string,
ascending: PropTypes.bool
})
}

static defaultProps = {
...SortableTable.defaultProps,
pageSize: 10,
sort: {
ascending: true
}
}

state = {
sort: this.props.sort,
expanded: [],
selected: [],
page: 0
}

sortedData = (rows, column, ascending) => orderBy(
rows,
[column],
[ascending ? 'asc' : 'desc']
);

sortBy = (column, ascending) => {
if (this.state.sort.column === column) {
this.setState({
sort: {
column,
ascending
}
});
} else {
this.setState({
sort: {
column,
ascending: true
}
});
}
};

get someSelected() {
return this.state.selected.length > 0;
}

get allSelected() {
return this.props.rows.length && (this.state.selected.length === this.props.rows.length);
}

selected(value) {
return includes(this.state.selected, value);
}

toggleSelection = (value) => {
const selected = this.state.selected;
const newSelection = includes(selected, value) ?
without(selected, value) :
[...selected, value];

this.setState({ selected: newSelection }, () => this.props.onSelect(newSelection));
};

toggleAll = () => {
const newSelection = this.allSelected ? [] : this.props.rows;

this.setState({ selected: newSelection },
this.props.onSelect(newSelection)
);
};

expanded(value) {
return includes(this.state.expanded, value);
}

toggleExpanded = (value) => {
const expanded = this.state.expanded;
const newExpanded = includes(expanded, value) ?
without(expanded, value) :
[...expanded, value];

this.setState({ expanded: newExpanded });
};

setPage = (page) => {
this.setState({ page });
}

componentWillReceiveProps(nextProps) {
// Clear selection if rows or selectable change
if (nextProps.rows !== this.props.rows ||
nextProps.selectable !== this.props.selectable) {
this.setState({ selected: [] });
}
if (nextProps.rows !== this.props.rows ||
nextProps.expandable !== this.props.expandable) {
this.setState({ expanded: [] });
}
}

render() {
const { page, sort } = this.state;
const { ascending, column } = sort;
const { columns, expandable, pageSize, paginated, rowClassName, rowExpanded, rows, selectable, onSelect, ...props } = this.props;
const cols = columns
.filter(col => !col.hidden)
.map(col => (col.sortable !== false) ?
{
active: column === col.key,
ascending,
onSort: asc => this.sortBy(col.key, asc),
...col
} : col
);

if (selectable) {
cols.unshift({
align: 'center',
key: 'select',
header: (
<input
type="checkbox"
className="mx-1"
checked={this.allSelected}
onChange={this.toggleAll}
/>
),
cell: row => (
<input
type="checkbox"
className="mx-1"
checked={this.selected(row)}
onChange={() => this.toggleSelection(row)}
/>
),
width: '1%'
});
}

if (expandable) {
cols.push({
align: 'center',
key: 'expand',
cell: row => (
<Button
className="px-2 py-0"
color="link"
onClick={() => this.toggleExpanded(row)}
>
<Icon name="ellipsis-v" size="lg" />
</Button>
),
width: '1%'
});
}

const start = page * pageSize;
const end = start + pageSize;
const sortedRows = this.sortedData(rows, column, ascending);
const visibleRows = paginated ? sortedRows.slice(start, end) : sortedRows;

return (
<div>
<SortableTable
{...props}
columns={cols}
rows={visibleRows}
rowClassName={row => classnames({ 'table-info': this.selected(row) }, rowClassName(row))}
rowExpanded={row => expandable && this.expanded(row) && rowExpanded(row)}
/>
{paginated && [
<hr />,
<Paginator
currentPage={page + 1}
onClick={pg => this.setPage(pg - 1)}
perPage={pageSize}
totalItems={rows.length}
/>
]}
</div>
);
}
}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import SummaryBoxItem from './components/SummaryBoxItem.js';
import Table from './components/Table.js';
import TimeInput from './components/TimeInput.js';
import Tooltip from './components/Tooltip.js';
import UncontrolledTable from './components/UncontrolledTable.js';
import ValidatedFormGroup from './components/ValidatedFormGroup.js';
import Waiting from './components/Waiting.js';

Expand Down Expand Up @@ -261,6 +262,7 @@ export {
SummaryBoxItem,
TimeInput,
Tooltip,
UncontrolledTable,
ValidatedFormGroup,
Waiting,
};
Loading

0 comments on commit 7e19f6f

Please sign in to comment.