Skip to content

Commit

Permalink
[#3] Filter by text as user types into filter box
Browse files Browse the repository at this point in the history
  • Loading branch information
amir-hadzic committed Nov 2, 2016
1 parent 45d42de commit de970ad
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 21 deletions.
5 changes: 5 additions & 0 deletions src/Filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createTextFilter } from './common';
const propTypes = {
value: PropTypes.array,
onChange: PropTypes.func.isRequired,
onTextChange: PropTypes.func.isRequired,
options: PropTypes.array.isRequired,
};

Expand All @@ -13,6 +14,7 @@ class Filter extends Component {
const {
value,
onChange,
onTextChange,
options,
} = this.props;
return (
Expand All @@ -22,6 +24,9 @@ class Filter extends Component {
placeholder="Search by text or tags"
promptTextCreator={(txt) => `Search for '${txt}'`}
onChange={(selected) => onChange(selected)}
onInputChange={(text) => onTextChange(text)}
onBlurResetsInput={false}
onCloseResetsInput={false}
newOptionCreator={({ label }) => createTextFilter(label)}
value={value}
multi
Expand Down
9 changes: 9 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const TABLE_NEW_DATA = 'sematable/TABLE_NEW_DATA';
export const TABLE_PAGE_CHANGED = 'sematable/TABLE_PAGE_CHANGED';
export const TABLE_PAGE_SIZE_CHANGED = 'sematable/TABLE_PAGE_SIZE_CHANGED';
export const TABLE_FILTER_CHANGED = 'sematable/TABLE_FILTER_CHANGED';
export const TABLE_FILTER_TEXT_CHANGED = 'sematable/TABLE_FILTER_TEXT_CHANGED';
export const TABLE_SORT_CHANGED = 'sematable/TABLE_SORT_CHANGED';
export const TABLE_ROW_CHECKED_CHANGED = 'sematable/TABLE_ROW_CHECKED_CHANGED';
export const TABLE_SELECT_ALL_CHANGED = 'sematable/TABLE_SELECT_ALL_CHANGED';
Expand Down Expand Up @@ -51,6 +52,14 @@ export const tableFilterChanged = (tableName, filter) => ({
},
});

export const tableFilterTextChanged = (tableName, filterText) => ({
type: TABLE_FILTER_TEXT_CHANGED,
payload: {
tableName,
filterText,
},
});

export const tableSortChanged = (tableName, sortKey) => ({
type: TABLE_SORT_CHANGED,
payload: {
Expand Down
6 changes: 6 additions & 0 deletions src/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TABLE_PAGE_SIZE_CHANGED,
TABLE_SORT_CHANGED,
TABLE_FILTER_CHANGED,
TABLE_FILTER_TEXT_CHANGED,
TABLE_SELECT_ALL_CHANGED,
TABLE_ROW_CHECKED_CHANGED,
TABLE_DESTROY_STATE,
Expand Down Expand Up @@ -90,6 +91,11 @@ const behaviours = {
page: 0,
filter: payload.filter,
}),
[TABLE_FILTER_TEXT_CHANGED]: (state, { payload }) => ({
...state,
page: 0,
filterText: payload.filterText,
}),
[TABLE_SELECT_ALL_CHANGED]: (state) => ({
...state,
selectAll: !state.selectAll,
Expand Down
64 changes: 43 additions & 21 deletions src/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,46 @@ function paginate(rows, { page, pageSize }) {
return rows.slice(start, start + pageSize);
}

function filter(rows = [], filters = [], columns) {
if (filters.length === 0) {
return rows.slice(0);
/**
* rows - original data
* filters - list of selected filters
* filterText - currently entered text in filter input
* columns - column definitions
*/
function filter(rows = [], filters = [], filterText, columns) {
let filteredRows = rows.slice(0);
if (filters.length === 0 && !filterText) {
return filteredRows;
}

// apply text filter across all columns
let filteredRows = _.filter(rows, row => _.some(columns, (column) => {
if (!column.filterable) {
return false;
}
const normalized = String(_.get(row, column.key)).toLowerCase();
return _.every(filters, f => !f.textFilter || normalized.indexOf(f.value) > -1);
}));

// apply value filters on taggable columns
filteredRows = _.filter(filteredRows, row => _.every(columns, column => {
if (!column.taggable) {
return true;
}
const value = _.get(row, column.key);
return _.every(filters, f => !f.valueFilter || f.key !== column.key || f.value === value);
}));
const textFilters = [
...(filterText ? [filterText] : []),
...filters.filter(f => f.textFilter).map(f => f.value),
];

const valueFilters = filters.filter(f => f.valueFilter);

if (textFilters.length > 0) {
// apply text filters across all columns
filteredRows = _.filter(rows, row => _.some(columns, (column) => {
if (!column.filterable) {
return false;
}
const normalized = String(_.get(row, column.key)).toLowerCase();
return _.every(textFilters, f => normalized.indexOf(f) > -1);
}));
}

if (valueFilters.length > 0) {
// apply value filters on taggable columns
filteredRows = _.filter(filteredRows, row => _.every(columns, column => {
if (!column.taggable) {
return true;
}
const value = _.get(row, column.key);
return _.every(valueFilters, f => f.key !== column.key || f.value === value);
}));
}

return filteredRows;
}
Expand Down Expand Up @@ -66,6 +84,7 @@ export default (tableName) => {
const getIsInitialized = (state) => state.sematable[tableName] !== undefined;
const getInitialData = (state) => tableProp(state, 'initialData');
const getFilter = (state) => tableProp(state, 'filter');
const getFilterText = (state) => tableProp(state, 'filterText');
const getColumns = (state) => tableProp(state, 'columns');
const getPage = (state) => tableProp(state, 'page');
const getPrimaryKey = (state) => tableProp(state, 'primaryKey');
Expand All @@ -81,8 +100,11 @@ export default (tableName) => {
const getFiltered = createSelector(
getInitialData,
getFilter,
getFilterText,
getColumns,
(initialData, textFilter, columns) => filter(initialData, textFilter, columns)
(initialData, filters, filterText, columns) => filter(
initialData, filters, filterText, columns
)
);

const getFilterOptions = createSelector(
Expand Down
5 changes: 5 additions & 0 deletions src/sematable.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
tablePageChanged,
tablePageSizeChanged,
tableFilterChanged,
tableFilterTextChanged,
tableSortChanged,
tableRowCheckedChanged,
tableSelectAllChanged,
Expand All @@ -34,6 +35,7 @@ const propTypes = {
onPageChange: PropTypes.func.isRequired,
onPageSizeChange: PropTypes.func.isRequired,
onFilterChange: PropTypes.func.isRequired,
onFilterTextChange: PropTypes.func.isRequired,
onHeaderClick: PropTypes.func.isRequired,
onInitialize: PropTypes.func.isRequired,
onNewData: PropTypes.func.isRequired,
Expand Down Expand Up @@ -83,6 +85,7 @@ const sematable = (tableName, TableComponent, columns, configs = {}) => {
onPageChange: (page) => dispatch(tablePageChanged(tableName, page)),
onPageSizeChange: (pageSize) => dispatch(tablePageSizeChanged(tableName, pageSize)),
onFilterChange: (filter) => dispatch(tableFilterChanged(tableName, filter)),
onFilterTextChange: (filterText) => dispatch(tableFilterTextChanged(tableName, filterText)),
onHeaderClick: (sortKey) => dispatch(tableSortChanged(tableName, sortKey)),
onNewData: (data) => dispatch(tableNewData(tableName, data)),
onNewFilterValue: (data) => dispatch(tableSetFilter(tableName, data)),
Expand Down Expand Up @@ -131,6 +134,7 @@ const sematable = (tableName, TableComponent, columns, configs = {}) => {
onPageChange,
onPageSizeChange,
onFilterChange,
onFilterTextChange,
onHeaderClick,
onRowCheckedChange,
onSelectAllChange,
Expand Down Expand Up @@ -183,6 +187,7 @@ const sematable = (tableName, TableComponent, columns, configs = {}) => {
value={filter}
options={filterOptions}
onChange={(f) => onFilterChange(f)}
onTextChange={(f) => onFilterTextChange(f)}
/>}
</div>
<div className="col-md-12">
Expand Down

0 comments on commit de970ad

Please sign in to comment.