• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gregnb / mui-datatables / #1880

01 Aug 2019 07:29AM UTC coverage: 74.963% (+3.7%) from 71.311%
#1880

push

gabrielliwerant
2.7.0

444 of 611 branches covered (72.67%)

Branch coverage included in aggregate %.

568 of 739 relevant lines covered (76.86%)

85.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.33
/src/components/TableFilter.js
1
import { Grid, GridList, GridListTile, TextField } from '@material-ui/core';
2
import Button from '@material-ui/core/Button';
3
import Checkbox from '@material-ui/core/Checkbox';
4
import FormControl from '@material-ui/core/FormControl';
5
import FormControlLabel from '@material-ui/core/FormControlLabel';
6
import FormGroup from '@material-ui/core/FormGroup';
7
import Input from '@material-ui/core/Input';
8
import InputLabel from '@material-ui/core/InputLabel';
9
import ListItemText from '@material-ui/core/ListItemText';
10
import MenuItem from '@material-ui/core/MenuItem';
11
import Select from '@material-ui/core/Select';
12
import { withStyles } from '@material-ui/core/styles';
13
import Typography from '@material-ui/core/Typography';
14
import classNames from 'classnames';
15
import PropTypes from 'prop-types';
16
import React from 'react';
17

18
export const defaultFilterStyles = theme => ({
1✔
19
  root: {
20
    backgroundColor: theme.palette.background.default,
21
    padding: '24px 24px 36px 24px',
22
    fontFamily: 'Roboto',
23
  },
24
  header: {
25
    flex: '0 0 auto',
26
    marginBottom: '16px',
27
    width: '100%',
28
    display: 'flex',
29
    justifyContent: 'space-between',
30
  },
31
  title: {
32
    display: 'inline-block',
33
    marginLeft: '7px',
34
    color: theme.palette.text.primary,
35
    fontSize: '14px',
36
    fontWeight: 500,
37
  },
38
  noMargin: {
39
    marginLeft: '0px',
40
  },
41
  reset: {
42
    alignSelf: 'left',
43
  },
44
  resetLink: {
45
    marginLeft: '16px',
46
    fontSize: '12px',
47
    cursor: 'pointer',
48
  },
49
  filtersSelected: {
50
    alignSelf: 'right',
51
  },
52
  /* checkbox */
53
  checkboxListTitle: {
54
    marginLeft: '7px',
55
    marginBottom: '8px',
56
    fontSize: '14px',
57
    color: theme.palette.text.secondary,
58
    textAlign: 'left',
59
    fontWeight: 500,
60
  },
61
  checkboxFormGroup: {
62
    marginTop: '8px',
63
  },
64
  checkboxFormControl: {
65
    margin: '0px',
66
  },
67
  checkboxFormControlLabel: {
68
    fontSize: '15px',
69
    marginLeft: '8px',
70
    color: theme.palette.text.primary,
71
  },
72
  checkboxIcon: {
73
    width: '32px',
74
    height: '32px',
75
  },
76
  checkbox: {
77
    '&$checked': {
78
      color: theme.palette.primary.main,
79
    },
80
  },
81
  checked: {},
82
  /* selects */
83
  selectRoot: {
84
    display: 'flex',
85
    marginTop: '16px',
86
    flexDirection: 'row',
87
    flexWrap: 'wrap',
88
    width: '100%',
89
    height: '80%',
90
    justifyContent: 'space-between',
91
  },
92
  selectFormControl: {
93
    flex: '1 1 calc(50% - 24px)',
94
  },
95
  /* textField */
96
  textFieldRoot: {
97
    display: 'flex',
98
    marginTop: '16px',
99
    flexDirection: 'row',
100
    flexWrap: 'wrap',
101
    width: '100%',
102
  },
103
  textFieldFormControl: {
104
    flex: '1 1 calc(50% - 24px)',
105
  },
106
});
107

108
class TableFilter extends React.Component {
109
  static propTypes = {
110
    /** Data used to populate filter dropdown/checkbox */
111
    filterData: PropTypes.array.isRequired,
112
    /** Data selected to be filtered against dropdown/checkbox */
113
    filterList: PropTypes.array.isRequired,
114
    /** Options used to describe table */
115
    options: PropTypes.object.isRequired,
116
    /** Callback to trigger filter update */
117
    onFilterUpdate: PropTypes.func,
118
    /** Callback to trigger filter reset */
119
    onFilterRest: PropTypes.func,
120
    /** Extend the style applied to components */
121
    classes: PropTypes.object,
122
  };
123

124
  handleCheckboxChange = (index, value, column) => {
125
    this.props.onFilterUpdate(index, value, column, 'checkbox');
1✔
126
  };
127

128
  handleDropdownChange = (event, index, column) => {
129
    const labelFilterAll = this.props.options.textLabels.filter.all;
3✔
130
    const value = event.target.value === labelFilterAll ? '' : event.target.value;
3✔
131
    this.props.onFilterUpdate(index, value, column, 'dropdown');
3✔
132
  };
133

134
  handleMultiselectChange = (index, value, column) => {
135
    this.props.onFilterUpdate(index, value, column, 'multiselect');
3✔
136
  };
137

138
  handleTextFieldChange = (event, index, column) => {
139
    this.props.onFilterUpdate(index, event.target.value, column, 'textField');
3✔
140
  };
141

142
  handleCustomChange = (value, index, column) => {
143
    this.props.onFilterUpdate(index, value, column.name, column.filterType);
×
144
  };
145

146
  renderCheckbox(column, index) {
147
    const { classes, filterData, filterList } = this.props;
16✔
148

149
    return (
16✔
150
      <GridListTile key={index} cols={2}>
151
        <FormGroup>
152
          <Grid item xs={12}>
153
            <Typography variant="body2" className={classes.checkboxListTitle}>
154
              {column.label}
155
            </Typography>
156
          </Grid>
157
          <Grid container>
158
            {filterData[index].map((filterValue, filterIndex) => (
159
              <Grid item key={filterIndex}>
52✔
160
                <FormControlLabel
161
                  key={filterIndex}
162
                  classes={{
163
                    root: classes.checkboxFormControl,
164
                    label: classes.checkboxFormControlLabel,
165
                  }}
166
                  control={
167
                    <Checkbox
168
                      className={classes.checkboxIcon}
169
                      onChange={this.handleCheckboxChange.bind(null, index, filterValue, column.name)}
170
                      checked={filterList[index].indexOf(filterValue) >= 0 ? true : false}
52!
171
                      classes={{
172
                        root: classes.checkbox,
173
                        checked: classes.checked,
174
                      }}
175
                      value={filterValue != null ? filterValue.toString() : ''}
52!
176
                    />
177
                  }
178
                  label={filterValue}
179
                />
180
              </Grid>
181
            ))}
182
          </Grid>
183
        </FormGroup>
184
      </GridListTile>
185
    );
186
  }
187

188
  renderSelect(column, index) {
189
    const { classes, filterData, filterList, options } = this.props;
8✔
190
    const textLabels = options.textLabels.filter;
8✔
191

192
    return (
8✔
193
      <GridListTile key={index} cols={1}>
194
        <div className={classes.selectRoot}>
195
          <FormControl className={classes.selectFormControl} key={index}>
196
            <InputLabel htmlFor={column.name}>{column.label}</InputLabel>
197
            <Select
198
              value={filterList[index].toString() || textLabels.all}
15✔
199
              name={column.name}
200
              onChange={event => this.handleDropdownChange(event, index, column.name)}
1✔
201
              input={<Input name={column.name} id={column.name} />}>
202
              <MenuItem value={textLabels.all} key={0}>
203
                {textLabels.all}
204
              </MenuItem>
205
              {filterData[index].map((filterValue, filterIndex) => (
206
                <MenuItem value={filterValue} key={filterIndex + 1}>
26✔
207
                  {filterValue != null ? filterValue.toString() : ''}
26!
208
                </MenuItem>
209
              ))}
210
            </Select>
211
          </FormControl>
212
        </div>
213
      </GridListTile>
214
    );
215
  }
216

217
  renderTextField(column, index) {
218
    const { classes, filterList } = this.props;
12✔
219

220
    return (
12✔
221
      <GridListTile key={index} cols={1}>
222
        <div className={classes.textFieldRoot}>
223
          <FormControl className={classes.textFieldFormControl} key={index}>
224
            <TextField
225
              label={column.label}
226
              value={filterList[index].toString() || ''}
24✔
227
              onChange={event => this.handleTextFieldChange(event, index, column.name)}
1✔
228
            />
229
          </FormControl>
230
        </div>
231
      </GridListTile>
232
    );
233
  }
234

235
  renderMultiselect(column, index) {
236
    const { classes, filterData, filterList } = this.props;
8✔
237

238
    return (
8✔
239
      <GridListTile key={index} cols={1}>
240
        <div className={classes.selectRoot}>
241
          <FormControl className={classes.selectFormControl} key={index}>
242
            <InputLabel htmlFor={column.name}>{column.label}</InputLabel>
243
            <Select
244
              multiple
245
              value={filterList[index] || []}
8!
246
              renderValue={selected => selected.join(', ')}
1✔
247
              name={column.name}
248
              onChange={event => this.handleMultiselectChange(index, event.target.value, column.name)}
1✔
249
              input={<Input name={column.name} id={column.name} />}>
250
              {filterData[index].map((filterValue, filterIndex) => (
251
                <MenuItem value={filterValue} key={filterIndex + 1}>
26✔
252
                  <Checkbox
253
                    checked={filterList[index].indexOf(filterValue) >= 0 ? true : false}
26✔
254
                    value={filterValue != null ? filterValue.toString() : ''}
26!
255
                    className={classes.checkboxIcon}
256
                    classes={{
257
                      root: classes.checkbox,
258
                      checked: classes.checked,
259
                    }}
260
                  />
261
                  <ListItemText primary={filterValue} />
262
                </MenuItem>
263
              ))}
264
            </Select>
265
          </FormControl>
266
        </div>
267
      </GridListTile>
268
    );
269
  }
270

271
  renderCustomField(column, index) {
272
    const { classes, filterList, options } = this.props;
4✔
273
    const display =
274
      (column.filterOptions && column.filterOptions.display) ||
4!
275
      (options.filterOptions && options.filterOptions.display);
276

277
    if (!display) {
4!
278
      console.error('Property "display" is required when using custom filter type.');
×
279
      return;
×
280
    }
281

282
    return (
4✔
283
      <GridListTile key={index} cols={1}>
284
        <div className={classes.textFieldRoot}>
285
          <FormControl key={index}>{display(filterList, this.handleCustomChange, index, column)}</FormControl>
286
        </div>
287
      </GridListTile>
288
    );
289
  }
290

291
  render() {
292
    const { classes, columns, options, onFilterReset } = this.props;
15✔
293
    const textLabels = options.textLabels.filter;
15✔
294
    const filterGridColumns = columns.filter(col => col.filter).length === 1 ? 1 : 2;
60!
295

296
    return (
15✔
297
      <div className={classes.root}>
298
        <div className={classes.header}>
299
          <div className={classes.reset}>
300
            <Typography
301
              variant="body2"
302
              className={classNames({
303
                [classes.title]: true,
304
              })}>
305
              {textLabels.title}
306
            </Typography>
307
            <Button
308
              color="primary"
309
              className={classes.resetLink}
310
              tabIndex={0}
311
              aria-label={textLabels.reset}
312
              data-testid={'filterReset-button'}
313
              onClick={onFilterReset}>
314
              {textLabels.reset}
315
            </Button>
316
          </div>
317
          <div className={classes.filtersSelected} />
318
        </div>
319
        <GridList cellHeight="auto" cols={filterGridColumns} spacing={34}>
320
          {columns.map((column, index) => {
321
            if (column.filter) {
60✔
322
              const filterType = column.filterType || options.filterType;
48✔
323
              return filterType === 'checkbox'
48✔
324
                ? this.renderCheckbox(column, index)
325
                : filterType === 'multiselect'
32✔
326
                ? this.renderMultiselect(column, index)
327
                : filterType === 'textField'
24✔
328
                ? this.renderTextField(column, index)
329
                : filterType === 'custom'
12✔
330
                ? this.renderCustomField(column, index)
331
                : this.renderSelect(column, index);
332
            }
333
          })}
334
        </GridList>
335
      </div>
336
    );
337
  }
338
}
339

340
export default withStyles(defaultFilterStyles, { name: 'MUIDataTableFilter' })(TableFilter);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc