Skip to content

Commit

Permalink
feat(react-grid): add the ability to prevent column filtering (#702)
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeyAlexeev authored Feb 8, 2018
1 parent 2cdfe06 commit f3d3f10
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';<%&additionalImports%>
import {
FilteringState,
IntegratedFiltering,
} from '@devexpress/dx-react-grid';
import {
Grid,
Table,
TableHeaderRow,
TableFilterRow,
} from '@devexpress/dx-react-grid-<%&themeName%>';

import {
generateRows,
} from '../../../demo-data/generator';

export default class Demo extends React.PureComponent {
constructor(props) {
super(props);

this.state = {
columns: [
{ name: 'name', title: 'Name' },
{ name: 'car', title: 'Car' },
{ name: 'sex', title: 'Sex' },
{ name: 'city', title: 'City' },
],
rows: generateRows({ length: 8 }),
defaultFilters: [{ columnName: 'car', value: 'cruze' }],
filteringStateColumnExtensions: [
{ columnName: 'name', filteringEnabled: false },
{ columnName: 'car', filteringEnabled: false },
],
};
}
render() {
const {
rows,
columns,
defaultFilters,
filteringStateColumnExtensions,
} = this.state;

return (
<<%&wrapperTag%>>
<Grid
rows={rows}
columns={columns}
>
<FilteringState
defaultFilters={defaultFilters}
columnExtensions={filteringStateColumnExtensions}
/>
<IntegratedFiltering />
<Table />
<TableHeaderRow />
<TableFilterRow />
</Grid>
</<%&wrapperTag%>>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import * as PropTypes from 'prop-types';

export const TableFilterCell = ({
style, filter, onFilter, children,
column, tableRow, tableColumn, getMessage,
column, tableRow, tableColumn, getMessage, filteringEnabled,
...restProps
}) => (
<th
style={{
fontWeight: 'normal',
verticalAlign: 'middle',
...style,
}}
{...restProps}
Expand All @@ -19,6 +20,7 @@ export const TableFilterCell = ({
className="form-control"
value={filter ? filter.value : ''}
onChange={e => onFilter(e.target.value ? { value: e.target.value } : null)}
readOnly={!filteringEnabled}
/>
)}
</th>
Expand All @@ -36,6 +38,7 @@ TableFilterCell.propTypes = {
tableRow: PropTypes.object,
tableColumn: PropTypes.object,
getMessage: PropTypes.func,
filteringEnabled: PropTypes.bool,
};

TableFilterCell.defaultProps = {
Expand All @@ -47,4 +50,5 @@ TableFilterCell.defaultProps = {
tableRow: undefined,
tableColumn: undefined,
getMessage: undefined,
filteringEnabled: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,13 @@ describe('TableFilterCell', () => {
expect(tree.is('.custom-class'))
.toBeTruthy();
});

it('should render readonly filtering editor if filtering is not allowed', () => {
const tree = shallow((
<TableFilterCell filteringEnabled={false} getMessage={key => key} />
));

expect(tree.find('input').prop('readOnly'))
.toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import Input from 'material-ui/Input';
import { TableCell } from 'material-ui/Table';
import { withStyles } from 'material-ui/styles';

const styles = theme => ({
const styles = ({ spacing }) => ({
cell: {
paddingRight: theme.spacing.unit,
paddingLeft: theme.spacing.unit,
paddingRight: spacing.unit,
paddingLeft: spacing.unit,
'&:first-child': {
paddingLeft: theme.spacing.unit * 3,
paddingLeft: spacing.unit * 3,
},
},
input: {
Expand All @@ -21,7 +21,7 @@ const styles = theme => ({
const TableFilterCellBase = ({
style, filter, getMessage, onFilter,
classes, children, className,
tableRow, tableColumn, column,
tableRow, tableColumn, column, filteringEnabled,
...restProps
}) => (
<TableCell
Expand All @@ -34,6 +34,7 @@ const TableFilterCellBase = ({
className={classes.input}
value={filter ? filter.value : ''}
placeholder={getMessage('filterPlaceholder')}
disabled={!filteringEnabled}
onChange={e => onFilter(e.target.value ? { value: e.target.value } : null)}
/>
)}
Expand All @@ -54,6 +55,7 @@ TableFilterCellBase.propTypes = {
tableRow: PropTypes.object,
tableColumn: PropTypes.object,
column: PropTypes.object,
filteringEnabled: PropTypes.bool,
};

TableFilterCellBase.defaultProps = {
Expand All @@ -65,6 +67,7 @@ TableFilterCellBase.defaultProps = {
tableRow: undefined,
tableColumn: undefined,
column: undefined,
filteringEnabled: true,
};

export const TableFilterCell = withStyles(styles, { name: 'TableFilterCell' })(TableFilterCellBase);
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,13 @@ describe('TableFilterCell', () => {
expect(tree.props().data)
.toMatchObject({ a: 1 });
});

it('should render disabled filtering editor if filtering is not allowed', () => {
const tree = shallow((
<TableFilterCell filteringEnabled={false} getMessage={key => key} />
));

expect(tree.find(Input).prop('disabled'))
.toBeTruthy();
});
});
14 changes: 10 additions & 4 deletions packages/dx-react-grid/docs/guides/filtering.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# React Grid - Filtering

The Grid component supports filtering data by a column value programmatically or using the value an end-user types in the corresponding Filter Row editor. The filtering state management, Filter Row rendering, and filtering logic are implemented in the related plugins.
The Grid component supports filtering data by a column value programmatically or using the value an end user types in the corresponding Filter Row editor. The filtering state management, Filter Row rendering, and filtering logic are implemented in the related plugins.

## Related Plugins

Expand Down Expand Up @@ -34,19 +34,25 @@ You can also specify a filtering predicate using the `IntegratedFiltering` plugi

.embedded-demo(grid-filtering/custom-filtering-algorithm)

### Disable Filtering by a Column

The [FilteringState](../reference/filtering-state.md) plugin's `columnExtensions` property allows you to prevent filtering by a specific column.

.embedded-demo(grid-filtering/disable-column-filtering)

## Remote Filtering

It is possible to perform filtering remotely by handling filtering state changes, generating a request, and sending it to the server.

Filtering options are updated once an end-user modifies a text within a Filter Row editor or other filtering control. Handle filtering option changes using the `FilteringState` plugin's `onFiltersChange` event and request data from the server using the applied filtering options. Once the filtered data is received from the server, pass it to the `Grid` component's `rows` property.
Filtering options are updated once an end user modifies the text in a Filter Row editor or other filtering control. Handle filtering option changes using the `FilteringState` plugin's `onFiltersChange` event and request data from the server using the applied filtering options. Once the filtered data is received from the server, pass it to the `Grid` component's `rows` property.

Note that in the case of remote filtering, you do not need to use the `IntegratedFiltering` plugin.
Note that you do not need to use the `IntegratedFiltering` plugin for remote filtering.

.embedded-demo(grid-filtering/remote-filtering)

## Customizing Filter Row Appearance

Pass a function that returns a custom component to the `TableFilterRow` plugin's `cellComponent` property to substitute the built-in filter row editors. In this case, you should also delegate the component's state management to the `TableFilterRow` plugin assigning the function's `filter` and `onFilter` arguments to the appropriate component's properties.
Pass a function that returns a custom component to the `TableFilterRow` plugin's `cellComponent` property to substitute the built-in filter row editors. In this case, delegate the component's state management to the `TableFilterRow` plugin by assigning the function's `filter` and `onFilter` arguments to the appropriate component's properties.

.embedded-demo(grid-filtering/custom-filter-row)

Expand Down
18 changes: 15 additions & 3 deletions packages/dx-react-grid/docs/reference/filtering-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ none

Name | Type | Default | Description
-----|------|---------|------------
filters | Array&lt;[Filter](#filter)&gt; | | Specifies the currently applied filters.
filters | Array&lt;[Filter](#filter)&gt; | | Specifies the applied filters.
defaultFilters | Array&lt;[Filter](#filter)&gt; | [] | Specifies the filters initially applied in the uncontrolled mode.
onFiltersChange | (filters: Array&lt;[Filter](#filter)&gt;) => void | | Handles filter changes.
columnFilteringEnabled | boolean | true | Specifies whether filtering is enabled for all columns.
columnExtensions | Array&lt;[FilteringStateColumnExtension](#filteringstatecolumnextension)&gt; | | Additional column properties that the plugin can handle.

## Interfaces

Expand All @@ -29,6 +31,15 @@ Field | Type | Description
columnName | string | Specifies the name of a column whose value is used for filtering.
value? | string | Specifies the filter value.

### FilteringStateColumnExtension

Describes additional column properties that the plugin can handle.

Field | Type | Description
------|------|------------
columnName | string | The name of a column to extend.
filteringEnabled | boolean | Specifies whether filtering is enabled for a column.

## Plugin Developer Reference

### Imports
Expand All @@ -39,5 +50,6 @@ none

Name | Plugin | Type | Description
-----|--------|------|------------
filters | Getter | Array&lt;[Filter](#filter)&gt; | The currently applied filters.
changeColumnFilter | Action | ({ columnName: string, config: Object }) => void | Adds, changes or removes a filter. Pass `null` to the `config` argument to remove the filter associated with the specified column.
filters | Getter | Array&lt;[Filter](#filter)&gt; | The applied filters.
isColumnFilteringEnabled | Getter | (columnName: string) => boolean | A function used to define if filtering by a column is enabled.
changeColumnFilter | Action | ({ columnName: string, config: Object }) => void | Adds, changes or removes a filter. Pass `null` to the `config` argument to remove the specified column's filter.
4 changes: 3 additions & 1 deletion packages/dx-react-grid/docs/reference/table-filter-row.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Field | Type | Description
filter | [Filter](filtering-state.md#filter) | Filtering options that are applied to a column.
onFilter | (filter: [Filter](filtering-state.md#filter)) => void | An event that initiates applying a new filter to a column.
column | [Column](grid.md#column) | A column.
filteringEnabled | boolean | Specifies whether filtering by a column is enabled.
getMessage | ([messageKey](#localization-messages): string) => string | Returns the filter editor placeholder text. Available in the "@devexpress/dx-react-grid-material-ui" package.

## Localization Messages
Expand All @@ -49,7 +50,7 @@ Name | Properties | Description
TableFilterRow.Cell | [TableFilterCellProps](#tablefiltercellprops) | A component that renders a filter row cell.
TableFilterRow.Row | [TableRowProps](table.md#tablerowprops) | A component that renders a filter row.

If you specify additional properties, they are added to the component's root element.
Additional properties are added to the component's root element.

## Plugin Developer Reference

Expand All @@ -59,6 +60,7 @@ Name | Plugin | Type | Description
-----|--------|------|------------
tableHeaderRows | Getter | Array&lt;[TableRow](table.md#tablerow)&gt; | Header rows to be rendered.
filters | Getter | Array&lt;[Filter](filtering-state.md#filter)&gt; | The filtering options.
isColumnFilteringEnabled | Getter | (columnName: string) => boolean | A function used to define if filtering by a column is enabled.
changeColumnFilter | Action | ({ columnName: string, config: Object }) => void | Changes a column filter or clears it if config is null.
tableCell | Template | [TableCellProps](table.md#tablecellprops) | A template that renders a table cell.
tableRow | Template | [TableRowProps](table.md#tablerowprops) | A template that renders a table row.
Expand Down
15 changes: 14 additions & 1 deletion packages/dx-react-grid/src/plugins/filtering-state.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { Getter, Action, Plugin } from '@devexpress/dx-react-core';
import { changeColumnFilter } from '@devexpress/dx-grid-core';
import { changeColumnFilter, getColumnExtensionValueGetter } from '@devexpress/dx-grid-core';

import { createStateHelper } from '../utils/state-helper';

const columnExtensionValueGetter = (columnExtensions, defaultValue) =>
getColumnExtensionValueGetter(columnExtensions, 'filteringEnabled', defaultValue);

export class FilteringState extends React.PureComponent {
constructor(props) {
super(props);
Expand Down Expand Up @@ -34,12 +38,17 @@ export class FilteringState extends React.PureComponent {
}
render() {
const { filters } = this.getState();
const { columnExtensions, columnFilteringEnabled } = this.props;

return (
<Plugin
name="FilteringState"
>
<Getter name="filters" value={filters} />
<Getter
name="isColumnFilteringEnabled"
value={columnExtensionValueGetter(columnExtensions, columnFilteringEnabled)}
/>
<Action name="changeColumnFilter" action={this.changeColumnFilter} />
</Plugin>
);
Expand All @@ -50,10 +59,14 @@ FilteringState.propTypes = {
filters: PropTypes.array,
defaultFilters: PropTypes.array,
onFiltersChange: PropTypes.func,
columnExtensions: PropTypes.array,
columnFilteringEnabled: PropTypes.bool,
};

FilteringState.defaultProps = {
filters: undefined,
defaultFilters: [],
onFiltersChange: undefined,
columnExtensions: undefined,
columnFilteringEnabled: true,
};
22 changes: 21 additions & 1 deletion packages/dx-react-grid/src/plugins/filtering-state.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import * as React from 'react';
import { mount } from 'enzyme';
import { setupConsole } from '@devexpress/dx-testing';
import { PluginHost } from '@devexpress/dx-react-core';
import { changeColumnFilter } from '@devexpress/dx-grid-core';
import { changeColumnFilter, getColumnExtensionValueGetter } from '@devexpress/dx-grid-core';
import { pluginDepsToComponents, getComputedState, executeComputedAction } from './test-utils';
import { FilteringState } from './filtering-state';

jest.mock('@devexpress/dx-grid-core', () => ({
changeColumnFilter: jest.fn(),
getColumnExtensionValueGetter: jest.fn(),
}));

const defaultDeps = {
Expand All @@ -28,6 +29,7 @@ describe('FilteringState', () => {

beforeEach(() => {
changeColumnFilter.mockImplementation(() => []);
getColumnExtensionValueGetter.mockImplementation(() => () => {});
});
afterEach(() => {
jest.resetAllMocks();
Expand Down Expand Up @@ -186,4 +188,22 @@ describe('FilteringState', () => {
.toHaveBeenCalledTimes(1);
});
});

describe('column extensions', () => {
it('should correctly call getColumnExtensionValueGetter', () => {
const columnExtensions = [{ columnName: 'a', filteringEnabled: true }];
mount((
<PluginHost>
{pluginDepsToComponents(defaultDeps)}
<FilteringState
columnFilteringEnabled={false}
columnExtensions={columnExtensions}
/>
</PluginHost>
));

expect(getColumnExtensionValueGetter)
.toBeCalledWith(columnExtensions, 'filteringEnabled', false);
});
});
});
6 changes: 4 additions & 2 deletions packages/dx-react-grid/src/plugins/table-filter-row.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class TableFilterRow extends React.PureComponent {
>
{params => (
<TemplateConnector>
{({ filters }, { changeColumnFilter }) => {
{({ filters, isColumnFilteringEnabled }, { changeColumnFilter }) => {
const { name: columnName } = params.tableColumn.column;
const filter = getColumnFilterConfig(filters, columnName);
const onFilter = config => changeColumnFilter({ columnName, config });
Expand All @@ -62,11 +62,13 @@ export class TableFilterRow extends React.PureComponent {
getMessage={getMessage}
column={params.tableColumn.column}
filter={filter}
filteringEnabled={isColumnFilteringEnabled(columnName)}
onFilter={onFilter}
>
{content}
</FilterCell>
)}
)
}
</TemplatePlaceholder>
);
}}
Expand Down
Loading

0 comments on commit f3d3f10

Please sign in to comment.