Skip to content

Commit f3d3f10

Browse files
feat(react-grid): add the ability to prevent column filtering (#702)
1 parent 2cdfe06 commit f3d3f10

File tree

12 files changed

+161
-18
lines changed

12 files changed

+161
-18
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';<%&additionalImports%>
2+
import {
3+
FilteringState,
4+
IntegratedFiltering,
5+
} from '@devexpress/dx-react-grid';
6+
import {
7+
Grid,
8+
Table,
9+
TableHeaderRow,
10+
TableFilterRow,
11+
} from '@devexpress/dx-react-grid-<%&themeName%>';
12+
13+
import {
14+
generateRows,
15+
} from '../../../demo-data/generator';
16+
17+
export default class Demo extends React.PureComponent {
18+
constructor(props) {
19+
super(props);
20+
21+
this.state = {
22+
columns: [
23+
{ name: 'name', title: 'Name' },
24+
{ name: 'car', title: 'Car' },
25+
{ name: 'sex', title: 'Sex' },
26+
{ name: 'city', title: 'City' },
27+
],
28+
rows: generateRows({ length: 8 }),
29+
defaultFilters: [{ columnName: 'car', value: 'cruze' }],
30+
filteringStateColumnExtensions: [
31+
{ columnName: 'name', filteringEnabled: false },
32+
{ columnName: 'car', filteringEnabled: false },
33+
],
34+
};
35+
}
36+
render() {
37+
const {
38+
rows,
39+
columns,
40+
defaultFilters,
41+
filteringStateColumnExtensions,
42+
} = this.state;
43+
44+
return (
45+
<<%&wrapperTag%>>
46+
<Grid
47+
rows={rows}
48+
columns={columns}
49+
>
50+
<FilteringState
51+
defaultFilters={defaultFilters}
52+
columnExtensions={filteringStateColumnExtensions}
53+
/>
54+
<IntegratedFiltering />
55+
<Table />
56+
<TableHeaderRow />
57+
<TableFilterRow />
58+
</Grid>
59+
</<%&wrapperTag%>>
60+
);
61+
}
62+
}

packages/dx-react-grid-bootstrap3/src/templates/table-filter-cell.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import * as PropTypes from 'prop-types';
33

44
export const TableFilterCell = ({
55
style, filter, onFilter, children,
6-
column, tableRow, tableColumn, getMessage,
6+
column, tableRow, tableColumn, getMessage, filteringEnabled,
77
...restProps
88
}) => (
99
<th
1010
style={{
1111
fontWeight: 'normal',
12+
verticalAlign: 'middle',
1213
...style,
1314
}}
1415
{...restProps}
@@ -19,6 +20,7 @@ export const TableFilterCell = ({
1920
className="form-control"
2021
value={filter ? filter.value : ''}
2122
onChange={e => onFilter(e.target.value ? { value: e.target.value } : null)}
23+
readOnly={!filteringEnabled}
2224
/>
2325
)}
2426
</th>
@@ -36,6 +38,7 @@ TableFilterCell.propTypes = {
3638
tableRow: PropTypes.object,
3739
tableColumn: PropTypes.object,
3840
getMessage: PropTypes.func,
41+
filteringEnabled: PropTypes.bool,
3942
};
4043

4144
TableFilterCell.defaultProps = {
@@ -47,4 +50,5 @@ TableFilterCell.defaultProps = {
4750
tableRow: undefined,
4851
tableColumn: undefined,
4952
getMessage: undefined,
53+
filteringEnabled: true,
5054
};

packages/dx-react-grid-bootstrap3/src/templates/table-filter-cell.test.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,13 @@ describe('TableFilterCell', () => {
3838
expect(tree.is('.custom-class'))
3939
.toBeTruthy();
4040
});
41+
42+
it('should render readonly filtering editor if filtering is not allowed', () => {
43+
const tree = shallow((
44+
<TableFilterCell filteringEnabled={false} getMessage={key => key} />
45+
));
46+
47+
expect(tree.find('input').prop('readOnly'))
48+
.toBeTruthy();
49+
});
4150
});

packages/dx-react-grid-material-ui/src/templates/table-filter-cell.jsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import Input from 'material-ui/Input';
55
import { TableCell } from 'material-ui/Table';
66
import { withStyles } from 'material-ui/styles';
77

8-
const styles = theme => ({
8+
const styles = ({ spacing }) => ({
99
cell: {
10-
paddingRight: theme.spacing.unit,
11-
paddingLeft: theme.spacing.unit,
10+
paddingRight: spacing.unit,
11+
paddingLeft: spacing.unit,
1212
'&:first-child': {
13-
paddingLeft: theme.spacing.unit * 3,
13+
paddingLeft: spacing.unit * 3,
1414
},
1515
},
1616
input: {
@@ -21,7 +21,7 @@ const styles = theme => ({
2121
const TableFilterCellBase = ({
2222
style, filter, getMessage, onFilter,
2323
classes, children, className,
24-
tableRow, tableColumn, column,
24+
tableRow, tableColumn, column, filteringEnabled,
2525
...restProps
2626
}) => (
2727
<TableCell
@@ -34,6 +34,7 @@ const TableFilterCellBase = ({
3434
className={classes.input}
3535
value={filter ? filter.value : ''}
3636
placeholder={getMessage('filterPlaceholder')}
37+
disabled={!filteringEnabled}
3738
onChange={e => onFilter(e.target.value ? { value: e.target.value } : null)}
3839
/>
3940
)}
@@ -54,6 +55,7 @@ TableFilterCellBase.propTypes = {
5455
tableRow: PropTypes.object,
5556
tableColumn: PropTypes.object,
5657
column: PropTypes.object,
58+
filteringEnabled: PropTypes.bool,
5759
};
5860

5961
TableFilterCellBase.defaultProps = {
@@ -65,6 +67,7 @@ TableFilterCellBase.defaultProps = {
6567
tableRow: undefined,
6668
tableColumn: undefined,
6769
column: undefined,
70+
filteringEnabled: true,
6871
};
6972

7073
export const TableFilterCell = withStyles(styles, { name: 'TableFilterCell' })(TableFilterCellBase);

packages/dx-react-grid-material-ui/src/templates/table-filter-cell.test.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,13 @@ describe('TableFilterCell', () => {
7171
expect(tree.props().data)
7272
.toMatchObject({ a: 1 });
7373
});
74+
75+
it('should render disabled filtering editor if filtering is not allowed', () => {
76+
const tree = shallow((
77+
<TableFilterCell filteringEnabled={false} getMessage={key => key} />
78+
));
79+
80+
expect(tree.find(Input).prop('disabled'))
81+
.toBeTruthy();
82+
});
7483
});

packages/dx-react-grid/docs/guides/filtering.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# React Grid - Filtering
22

3-
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.
3+
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.
44

55
## Related Plugins
66

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

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

37+
### Disable Filtering by a Column
38+
39+
The [FilteringState](../reference/filtering-state.md) plugin's `columnExtensions` property allows you to prevent filtering by a specific column.
40+
41+
.embedded-demo(grid-filtering/disable-column-filtering)
42+
3743
## Remote Filtering
3844

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

41-
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.
47+
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.
4248

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

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

4753
## Customizing Filter Row Appearance
4854

49-
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.
55+
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.
5056

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

packages/dx-react-grid/docs/reference/filtering-state.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ none
1212

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

1921
## Interfaces
2022

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

34+
### FilteringStateColumnExtension
35+
36+
Describes additional column properties that the plugin can handle.
37+
38+
Field | Type | Description
39+
------|------|------------
40+
columnName | string | The name of a column to extend.
41+
filteringEnabled | boolean | Specifies whether filtering is enabled for a column.
42+
3243
## Plugin Developer Reference
3344

3445
### Imports
@@ -39,5 +50,6 @@ none
3950

4051
Name | Plugin | Type | Description
4152
-----|--------|------|------------
42-
filters | Getter | Array&lt;[Filter](#filter)&gt; | The currently applied filters.
43-
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.
53+
filters | Getter | Array&lt;[Filter](#filter)&gt; | The applied filters.
54+
isColumnFilteringEnabled | Getter | (columnName: string) => boolean | A function used to define if filtering by a column is enabled.
55+
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.

packages/dx-react-grid/docs/reference/table-filter-row.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Field | Type | Description
3232
filter | [Filter](filtering-state.md#filter) | Filtering options that are applied to a column.
3333
onFilter | (filter: [Filter](filtering-state.md#filter)) => void | An event that initiates applying a new filter to a column.
3434
column | [Column](grid.md#column) | A column.
35+
filteringEnabled | boolean | Specifies whether filtering by a column is enabled.
3536
getMessage | ([messageKey](#localization-messages): string) => string | Returns the filter editor placeholder text. Available in the "@devexpress/dx-react-grid-material-ui" package.
3637

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

52-
If you specify additional properties, they are added to the component's root element.
53+
Additional properties are added to the component's root element.
5354

5455
## Plugin Developer Reference
5556

@@ -59,6 +60,7 @@ Name | Plugin | Type | Description
5960
-----|--------|------|------------
6061
tableHeaderRows | Getter | Array&lt;[TableRow](table.md#tablerow)&gt; | Header rows to be rendered.
6162
filters | Getter | Array&lt;[Filter](filtering-state.md#filter)&gt; | The filtering options.
63+
isColumnFilteringEnabled | Getter | (columnName: string) => boolean | A function used to define if filtering by a column is enabled.
6264
changeColumnFilter | Action | ({ columnName: string, config: Object }) => void | Changes a column filter or clears it if config is null.
6365
tableCell | Template | [TableCellProps](table.md#tablecellprops) | A template that renders a table cell.
6466
tableRow | Template | [TableRowProps](table.md#tablerowprops) | A template that renders a table row.

packages/dx-react-grid/src/plugins/filtering-state.jsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import * as React from 'react';
22
import * as PropTypes from 'prop-types';
33
import { Getter, Action, Plugin } from '@devexpress/dx-react-core';
4-
import { changeColumnFilter } from '@devexpress/dx-grid-core';
4+
import { changeColumnFilter, getColumnExtensionValueGetter } from '@devexpress/dx-grid-core';
5+
56
import { createStateHelper } from '../utils/state-helper';
67

8+
const columnExtensionValueGetter = (columnExtensions, defaultValue) =>
9+
getColumnExtensionValueGetter(columnExtensions, 'filteringEnabled', defaultValue);
10+
711
export class FilteringState extends React.PureComponent {
812
constructor(props) {
913
super(props);
@@ -34,12 +38,17 @@ export class FilteringState extends React.PureComponent {
3438
}
3539
render() {
3640
const { filters } = this.getState();
41+
const { columnExtensions, columnFilteringEnabled } = this.props;
3742

3843
return (
3944
<Plugin
4045
name="FilteringState"
4146
>
4247
<Getter name="filters" value={filters} />
48+
<Getter
49+
name="isColumnFilteringEnabled"
50+
value={columnExtensionValueGetter(columnExtensions, columnFilteringEnabled)}
51+
/>
4352
<Action name="changeColumnFilter" action={this.changeColumnFilter} />
4453
</Plugin>
4554
);
@@ -50,10 +59,14 @@ FilteringState.propTypes = {
5059
filters: PropTypes.array,
5160
defaultFilters: PropTypes.array,
5261
onFiltersChange: PropTypes.func,
62+
columnExtensions: PropTypes.array,
63+
columnFilteringEnabled: PropTypes.bool,
5364
};
5465

5566
FilteringState.defaultProps = {
5667
filters: undefined,
5768
defaultFilters: [],
5869
onFiltersChange: undefined,
70+
columnExtensions: undefined,
71+
columnFilteringEnabled: true,
5972
};

packages/dx-react-grid/src/plugins/filtering-state.test.jsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import * as React from 'react';
22
import { mount } from 'enzyme';
33
import { setupConsole } from '@devexpress/dx-testing';
44
import { PluginHost } from '@devexpress/dx-react-core';
5-
import { changeColumnFilter } from '@devexpress/dx-grid-core';
5+
import { changeColumnFilter, getColumnExtensionValueGetter } from '@devexpress/dx-grid-core';
66
import { pluginDepsToComponents, getComputedState, executeComputedAction } from './test-utils';
77
import { FilteringState } from './filtering-state';
88

99
jest.mock('@devexpress/dx-grid-core', () => ({
1010
changeColumnFilter: jest.fn(),
11+
getColumnExtensionValueGetter: jest.fn(),
1112
}));
1213

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

2930
beforeEach(() => {
3031
changeColumnFilter.mockImplementation(() => []);
32+
getColumnExtensionValueGetter.mockImplementation(() => () => {});
3133
});
3234
afterEach(() => {
3335
jest.resetAllMocks();
@@ -186,4 +188,22 @@ describe('FilteringState', () => {
186188
.toHaveBeenCalledTimes(1);
187189
});
188190
});
191+
192+
describe('column extensions', () => {
193+
it('should correctly call getColumnExtensionValueGetter', () => {
194+
const columnExtensions = [{ columnName: 'a', filteringEnabled: true }];
195+
mount((
196+
<PluginHost>
197+
{pluginDepsToComponents(defaultDeps)}
198+
<FilteringState
199+
columnFilteringEnabled={false}
200+
columnExtensions={columnExtensions}
201+
/>
202+
</PluginHost>
203+
));
204+
205+
expect(getColumnExtensionValueGetter)
206+
.toBeCalledWith(columnExtensions, 'filteringEnabled', false);
207+
});
208+
});
189209
});

0 commit comments

Comments
 (0)