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

gregnb / mui-datatables / #1837

30 Aug 2018 08:51PM UTC coverage: 77.985% (+4.8%) from 73.194%
#1837

push

gregnb
published version 2.0.0-beta-27

235 of 329 branches covered (71.43%)

Branch coverage included in aggregate %.

392 of 475 relevant lines covered (82.53%)

34.84 hits per line

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

76.77
/src/MUIDataTable.js
1
import React from "react";
2
import PropTypes from "prop-types";
3
import Paper from "@material-ui/core/Paper";
4
import Table from "@material-ui/core/Table";
5
import MUIDataTableToolbar from "./MUIDataTableToolbar";
6
import MUIDataTableToolbarSelect from "./MUIDataTableToolbarSelect";
7
import MUIDataTableFilterList from "./MUIDataTableFilterList";
8
import MUIDataTableBody from "./MUIDataTableBody";
9
import MUIDataTableResize from "./MUIDataTableResize";
10
import MUIDataTableHead from "./MUIDataTableHead";
11
import MUIDataTablePagination from "./MUIDataTablePagination";
12
import cloneDeep from "lodash.clonedeep";
13
import merge from "lodash.merge";
14
import textLabels from "./textLabels";
15
import { withStyles } from "@material-ui/core/styles";
16

17
const defaultTableStyles = {
1✔
18
  root: {},
19
  responsiveScroll: {
20
    overflowX: "auto",
21
  },
22
  caption: {
23
    position: "absolute",
24
    left: "-1000px",
25
  },
26
  liveAnnounce: {
27
    border: "0",
28
    clip: "rect(0 0 0 0)",
29
    height: "1px",
30
    margin: "-1px",
31
    overflow: "hidden",
32
    padding: "0",
33
    position: "absolute",
34
    width: "1px",
35
  },
36
};
37

38
const TABLE_LOAD = {
1✔
39
  INITIAL: 1,
40
  UPDATE: 2,
41
};
42

43
class MUIDataTable extends React.Component {
44
  static propTypes = {
45
    /** Title of the table */
46
    title: PropTypes.string.isRequired,
47
    /** Data used to describe table */
48
    data: PropTypes.array.isRequired,
49
    /** Columns used to describe table */
50
    columns: PropTypes.PropTypes.arrayOf(
51
      PropTypes.oneOfType([
52
        PropTypes.string,
53
        PropTypes.shape({
54
          name: PropTypes.string.isRequired,
55
          options: PropTypes.shape({
56
            display: PropTypes.bool,
57
            filter: PropTypes.bool,
58
            sort: PropTypes.bool,
59
            customHeadRender: PropTypes.func,
60
            customBodyRender: PropTypes.func,
61
          }),
62
        }),
63
      ]),
64
    ).isRequired,
65
    /** Options used to describe table */
66
    options: PropTypes.shape({
67
      responsive: PropTypes.oneOf(["stacked", "scroll"]),
68
      filterType: PropTypes.oneOf(["dropdown", "checkbox", "multiselect"]),
69
      textLabels: PropTypes.object,
70
      pagination: PropTypes.bool,
71
      customToolbar: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
72
      customToolbarSelect: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
73
      customFooter: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
74
      onRowClick: PropTypes.func,
75
      resizableColumns: PropTypes.bool,
76
      selectableRows: PropTypes.bool,
77
      serverSide: PropTypes.bool,
78
      caseSensitive: PropTypes.bool,
79
      rowHover: PropTypes.bool,
80
      page: PropTypes.number,
81
      count: PropTypes.number,
82
      filterList: PropTypes.array,
83
      rowsSelected: PropTypes.array,
84
      rowsPerPage: PropTypes.number,
85
      rowsPerPageOptions: PropTypes.array,
86
      filter: PropTypes.bool,
87
      sort: PropTypes.bool,
88
      search: PropTypes.bool,
89
      print: PropTypes.bool,
90
      viewColumns: PropTypes.bool,
91
      download: PropTypes.bool,
92
    }),
93
    /** Pass and use className to style MUIDataTable as desired */
94
    className: PropTypes.string,
95
  };
96

97
  static defaultProps = {
98
    title: "",
99
    options: {},
100
    data: [],
101
    columns: [],
102
  };
103

104
  state = {
105
    announceText: null,
106
    activeColumn: null,
107
    data: [],
108
    displayData: [],
109
    page: 0,
110
    rowsPerPage: 0,
111
    columns: [],
112
    filterData: [],
113
    filterList: [],
114
    selectedRows: {
115
      data: [],
116
      lookup: {},
117
    },
118
    showResponsive: false,
119
    searchText: null,
120
  };
121

122
  constructor() {
123
    super();
124
    this.tableRef = false;
26✔
125
    this.headCellRefs = {};
26✔
126
    this.setHeadResizeable = () => {};
26✔
127
  }
26✔
128

129
  componentWillMount() {
130
    this.initializeTable(this.props);
131
  }
26✔
132

133
  componentDidMount() {
134
    this.setHeadResizeable(this.headCellRefs, this.tableRef);
135
  }
26✔
136

137
  componentWillReceiveProps(nextProps) {
138
    if (this.props.data !== nextProps.data || this.props.columns !== nextProps.columns) {
139
      this.initializeTable(nextProps);
1!
140
    }
×
141
  }
142

143
  initializeTable(props) {
144
    this.getDefaultOptions(props);
145
    this.setTableOptions(props);
26✔
146
    this.setTableData(props, TABLE_LOAD.INITIAL);
26✔
147
  }
26✔
148

149
  /*
150
   * React currently does not support deep merge for defaultProps. Objects are overwritten
151
   */
152
  getDefaultOptions(props) {
153
    const defaultOptions = {
154
      responsive: "stacked",
26✔
155
      filterType: "checkbox",
156
      pagination: true,
157
      textLabels,
158
      resizableColumns: false,
159
      selectableRows: true,
160
      caseSensitive: false,
161
      serverSide: false,
162
      rowHover: true,
163
      rowsPerPage: 10,
164
      rowsPerPageOptions: [10, 15, 100],
165
      filter: true,
166
      sortFilterList: true,
167
      sort: true,
168
      search: true,
169
      print: true,
170
      viewColumns: true,
171
      download: true,
172
    };
173

174
    this.options = merge(defaultOptions, props.options);
175
  }
26✔
176

177
  validateOptions(options) {
178
    if (options.serverSide && options.onServerRequest === undefined) {
179
      throw Error("onServerRequest callback must be provided when using serverSide option");
26!
180
    }
×
181
  }
182

183
  setServerRequest = action => {
184
    if (this.options.serverSide) {
185
      this.options.onServerRequest(action, this.state);
12!
186
    }
×
187
  };
188

189
  setTableOptions(props) {
190
    const optionNames = ["rowsPerPage", "page", "filterList", "rowsPerPageOptions"];
191
    const optState = optionNames.reduce((acc, cur) => {
26✔
192
      if (this.options[cur]) {
26✔
193
        acc[cur] = this.options[cur];
104✔
194
      }
52✔
195
      return acc;
196
    }, {});
104✔
197

198
    this.validateOptions(optState);
199
    this.setState(optState);
26✔
200
  }
26✔
201

202
  setHeadCellRef = (index, el) => {
203
    this.headCellRefs[index] = el;
204
  };
25✔
205

206
  /*
207
   *  Build the source table data
208
   */
209

210
  setTableData(props, status) {
211
    const { data, columns, options } = props;
212

26✔
213
    let columnData = [],
214
      filterData = [],
26✔
215
      filterList = [],
26✔
216
      tableData = [];
26✔
217

26✔
218
    columns.forEach((column, colIndex) => {
219
      let columnOptions = {
26✔
220
        display: true,
104✔
221
        filter: true,
222
        sort: true,
223
        sortDirection: null,
224
      };
225

226
      if (typeof column === "object") {
227
        columnOptions = {
104✔
228
          name: column.name,
78✔
229
          ...columnOptions,
230
          ...(column.options ? column.options : {}),
231
        };
78✔
232
      } else {
233
        columnOptions = { ...columnOptions, name: column };
234
      }
26✔
235

236
      columnData.push(columnOptions);
237

238
      filterData[colIndex] = [];
104!
239
      filterList[colIndex] = [];
×
240

241
      for (let rowIndex = 0; rowIndex < data.length; rowIndex++) {
242
        let value = status === TABLE_LOAD.INITIAL ? data[rowIndex][colIndex] : data[rowIndex].data[colIndex];
104✔
243

244
        if (typeof tableData[rowIndex] === "undefined") {
104✔
245
          tableData.push({
104✔
246
            index: status === TABLE_LOAD.INITIAL ? rowIndex : data[rowIndex].index,
247
            data: status === TABLE_LOAD.INITIAL ? data[rowIndex] : data[rowIndex].data,
104✔
248
          });
416!
249
        }
250

416✔
251
        if (typeof columnOptions.customBodyRender === "function") {
104✔
252
          const tableMeta = this.getCustomRenderMeta(rowIndex, colIndex, value, [], columnData, this.state);
104!
253
          const funcResult = columnOptions.customBodyRender(value, tableMeta);
104!
254

255
          if (React.isValidElement(funcResult) && funcResult.props.value) {
256
            value = funcResult.props.value;
257
          } else if (typeof funcResult === "string") {
416✔
258
            value = funcResult;
208✔
259
          }
208✔
260
        }
261

208✔
262
        if (filterData[colIndex].indexOf(value) < 0) filterData[colIndex].push(value);
104✔
263
      }
104!
264

104✔
265
      if (this.options.sortFilterList) {
266
        const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
267
        filterData[colIndex].sort(collator.compare);
268
      }
416✔
269
    });
270

271
    if (options.filterList) filterList = options.filterList;
104!
272

104✔
273
    if (filterList.length !== columns.length) {
104✔
274
      throw new Error("Provided options.filterList does not match the column length");
275
    }
276

277
    let selectedRowsData = {
26!
278
      data: [],
279
      lookup: {},
26!
280
    };
×
281

282
    if (TABLE_LOAD.INITIAL) {
283
      if (options.rowsSelected && options.rowsSelected.length) {
26✔
284
        options.rowsSelected.forEach(row => {
285
          selectedRowsData.data.push({ index: row, dataIndex: row });
286
          selectedRowsData.lookup[row] = true;
287
        });
288
      }
26!
289
    }
26!
290

×
291
    /* set source data and display Data set source set */
×
292
    this.setState(prevState => ({
×
293
      columns: columnData,
294
      filterData: filterData,
295
      filterList: filterList,
296
      selectedRows: selectedRowsData,
297
      data: tableData,
298
      displayData: this.getDisplayData(columnData, tableData, filterList, prevState.searchText),
26✔
299
    }));
300
  }
301

302
  /*
303
   *  Build the table data used to display to the user (ie: after filter/search applied)
304
   */
305
  computeDisplayRow(columns, row, rowIndex, filterList, searchText) {
306
    let isFiltered = false;
307
    let isSearchFound = false;
308
    let displayRow = [];
309

310
    for (let index = 0; index < row.length; index++) {
311
      let columnDisplay = row[index];
312
      let columnValue = row[index];
152✔
313

152✔
314
      if (columns[index].customBodyRender) {
152✔
315
        const tableMeta = this.getCustomRenderMeta(rowIndex, index, row, columns[index], this.state.data, {
316
          ...this.state,
152✔
317
          filterList: filterList,
608✔
318
          searchText: searchText,
608✔
319
        });
320

608✔
321
        const funcResult = columns[index].customBodyRender(
296✔
322
          columnValue,
323
          tableMeta,
324
          this.updateDataCol.bind(null, rowIndex, index),
325
        );
326
        columnDisplay = funcResult;
327

296✔
328
        /* drill down to get the value of a cell */
329
        columnValue =
330
          typeof funcResult === "string"
331
            ? funcResult
332
            : funcResult.props && funcResult.props.value
296✔
333
              ? funcResult.props.value
334
              : columnValue;
335
      }
296✔
336

296✔
337
      displayRow.push(columnDisplay);
338

444!
339
      if (filterList[index].length && filterList[index].indexOf(columnValue) < 0) {
340
        isFiltered = true;
341
      }
342

343
      const columnVal = columnValue === null ? "" : columnValue.toString();
608✔
344
      const searchCase = !this.options.caseSensitive ? columnVal.toLowerCase() : columnVal.toString();
345

608✔
346
      if (searchText && searchCase.indexOf(searchText.toLowerCase()) >= 0) {
20✔
347
        isSearchFound = true;
348
      }
349
    }
608!
350

608!
351
    if (isFiltered || (searchText && !isSearchFound)) return null;
352
    else return displayRow;
608✔
353
  }
1✔
354

355
  updateDataCol = (row, index, value) => {
356
    this.setState(prevState => {
357
      let changedData = cloneDeep(prevState.data);
152✔
358
      let filterData = cloneDeep(prevState.filterData);
129✔
359

360
      const tableMeta = this.getCustomRenderMeta(row, index, row, prevState.columns[index], prevState.data, prevState);
361
      const funcResult = prevState.columns[index].customBodyRender(value, tableMeta);
362

1✔
363
      const filterValue =
1✔
364
        React.isValidElement(funcResult) && funcResult.props.value
1✔
365
          ? funcResult.props.value
366
          : prevState["data"][row][index];
1✔
367

1✔
368
      const prevFilterIndex = filterData[index].indexOf(filterValue);
369
      filterData[index].splice(prevFilterIndex, 1, filterValue);
370

1!
371
      changedData[row].data[index] = value;
372

373
      if (this.options.sortFilterList) {
374
        const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
1✔
375
        filterData[index].sort(collator.compare);
1✔
376
      }
377

1✔
378
      return {
379
        data: changedData,
1!
380
        filterData: filterData,
1✔
381
        displayData: this.getDisplayData(prevState.columns, changedData, prevState.filterList, prevState.searchText),
1✔
382
      };
383
    });
384
  };
1✔
385

386
  getCustomRenderMeta = (rowIndex, colIndex, rowData, columnData, tableData, curState) => {
387
    const { columns, data, displayData, filterData, ...tableState } = curState;
388

389
    return {
390
      rowIndex: rowIndex,
391
      columnIndex: colIndex,
392
      columnData: columnData,
393
      rowData: rowData,
505✔
394
      tableData: tableData,
395
      tableState: tableState,
505✔
396
    };
397
  };
398

399
  getDisplayData(columns, data, filterList, searchText) {
400
    let newRows = [];
401

402
    for (let index = 0; index < data.length; index++) {
403
      const value = data[index].data;
404
      const displayRow = this.computeDisplayRow(columns, value, index, filterList, searchText);
405

406
      if (displayRow) {
38✔
407
        newRows.push({
408
          data: displayRow,
38✔
409
          dataIndex: data[index].index,
152✔
410
        });
152✔
411
      }
412
    }
152✔
413

129✔
414
    return newRows;
415
  }
416

417
  toggleViewColumn = index => {
418
    this.setState(
419
      prevState => {
420
        const columns = cloneDeep(prevState.columns);
38✔
421
        columns[index].display = !columns[index].display;
422
        return {
423
          columns: columns,
424
        };
1✔
425
      },
426
      () => {
1✔
427
        if (this.options.onColumnViewChange) {
1!
428
          this.options.onColumnViewChange(
×
429
            this.state.columns[index].name,
430
            this.state.columns[index].display ? "add" : "remove",
1✔
431
          );
1✔
432
        }
433
      },
434
    );
435
  };
436

1!
437
  getSortDirection(column) {
×
438
    return column.sortDirection === "asc" ? "ascending" : "descending";
439
  }
×
440

441
  toggleSortColumn = index => {
442
    this.setState(
443
      prevState => {
444
        let columns = cloneDeep(prevState.columns);
445
        let data = prevState.data;
446
        const order = prevState.columns[index].sortDirection;
447

1!
448
        for (let pos = 0; pos < columns.length; pos++) {
449
          if (index !== pos) {
450
            columns[pos].sortDirection = null;
451
          } else {
1✔
452
            columns[pos].sortDirection = columns[pos].sortDirection === "asc" ? "desc" : "asc";
453
          }
1✔
454
        }
1✔
455

1✔
456
        const orderLabel = this.getSortDirection(columns[index]);
457
        const announceText = `Table now sorted by ${columns[index].name} : ${orderLabel}`;
1✔
458

4✔
459
        let newState = {
3✔
460
          columns: columns,
461
          announceText: announceText,
1!
462
          activeColumn: index,
463
        };
464

465
        if (this.options.serverSide) {
1✔
466
          newState = {
1✔
467
            ...newState,
468
            data: prevState.data,
1✔
469
            displayData: prevState.displayData,
470
            selectedRows: prevState.selectedRows,
471
          };
472
        } else {
473
          const sortedData = this.sortTable(data, index, order);
474

1!
475
          newState = {
×
476
            ...newState,
477
            data: sortedData.data,
478
            displayData: this.getDisplayData(columns, sortedData.data, prevState.filterList, prevState.searchText),
479
            selectedRows: sortedData.selectedRows,
480
          };
481
        }
482

1✔
483
        return newState;
484
      },
1✔
485
      () => {
486
        this.setServerRequest("sort");
487
        if (this.options.onColumnSortChange) {
488
          this.options.onColumnSortChange(
489
            this.state.columns[index].name,
490
            this.getSortDirection(this.state.columns[index]),
491
          );
492
        }
1✔
493
      },
494
    );
495
  };
1✔
496

1!
497
  changeRowsPerPage = rows => {
×
498
    this.setState(
499
      () => ({
500
        rowsPerPage: rows,
501
      }),
502
      () => {
503
        this.setServerRequest("changeRowsPerPage");
504
        if (this.options.onChangeRowsPerPage) {
505
          this.options.onChangeRowsPerPage(this.state.rowsPerPage);
506
        }
507
      },
1✔
508
    );
1✔
509
  };
510

511
  changePage = page => {
512
    this.setState(
1✔
513
      () => ({
1!
514
        page: page,
×
515
      }),
516
      () => {
517
        this.setServerRequest("changePage");
518
        if (this.options.onChangePage) {
519
          this.options.onChangePage(this.state.page);
520
        }
521
      },
1✔
522
    );
1✔
523
  };
524

525
  searchTextUpdate = text => {
526
    this.setState(
1✔
527
      prevState => ({
1!
528
        searchText: text && text.length ? text : null,
×
529
        displayData: this.options.serverSide
530
          ? prevState.displayData
531
          : this.getDisplayData(prevState.columns, prevState.data, prevState.filterList, text),
532
      }),
533
      () => {
534
        this.setServerRequest("search");
535
      },
1✔
536
    );
1✔
537
  };
3!
538

1!
539
  resetFilters = () => {
540
    this.setState(
541
      prevState => {
542
        const filterList = prevState.columns.map((column, index) => []);
543

1✔
544
        return {
545
          filterList: filterList,
546
          displayData: this.options.serverSide
547
            ? prevState.displayData
548
            : this.getDisplayData(prevState.columns, prevState.data, filterList, prevState.searchText),
549
        };
1✔
550
      },
551
      () => {
4✔
552
        this.setServerRequest("resetFilters");
553
        if (this.options.onFilterChange) {
1✔
554
          this.options.onFilterChange(null, this.state.filterList);
555
        }
1!
556
      },
557
    );
558
  };
559

560
  filterUpdate = (index, column, type) => {
561
    this.setState(
1✔
562
      prevState => {
1!
563
        const filterList = cloneDeep(prevState.filterList);
×
564
        const filterPos = filterList[index].indexOf(column);
565

566
        switch (type) {
567
          case "checkbox":
568
            filterPos >= 0 ? filterList[index].splice(filterPos, 1) : filterList[index].push(column);
569
            break;
570
          case "multiselect":
7✔
571
            filterList[index] = column === "" ? [] : column;
572
            break;
7✔
573
          default:
7✔
574
            filterList[index] = filterPos >= 0 || column === "" ? [] : [column];
575
        }
7!
576

577
        return {
4✔
578
          filterList: filterList,
4✔
579
          displayData: this.options.serverSide
580
            ? prevState.displayData
×
581
            : this.getDisplayData(prevState.columns, prevState.data, filterList, prevState.searchText),
×
582
        };
583
      },
3✔
584
      () => {
585
        this.setServerRequest("filterChange");
586
        if (this.options.onFilterChange) {
7✔
587
          this.options.onFilterChange(column, this.state.filterList);
588
        }
7!
589
      },
590
    );
591
  };
592

593
  selectRowDelete = () => {
594
    const { selectedRows, data, filterList } = this.state;
7✔
595

7!
596
    const selectedMap = this.buildSelectedMap(selectedRows.data);
×
597
    const cleanRows = data.filter((_, index) => !selectedMap[index]);
598

599
    if (this.options.onRowsDelete) {
600
      this.options.onRowsDelete(selectedRows);
601
    }
602

603
    this.setTableData(
×
604
      {
605
        columns: this.props.columns,
×
606
        data: cleanRows,
×
607
        options: {
608
          filterList: filterList,
×
609
        },
×
610
      },
611
      TABLE_LOAD.UPDATE,
612
    );
×
613
  };
614

615
  buildSelectedMap = rows => {
616
    return rows.reduce((accum, { index }) => {
617
      accum[index] = true;
618
      return accum;
619
    }, {});
620
  };
621

622
  selectRowUpdate = (type, value) => {
623
    if (type === "head") {
624
      this.setState(
625
        prevState => {
3✔
626
          const { data } = prevState;
5✔
627
          const selectedRowsLen = prevState.selectedRows.data.length;
5✔
628
          const isDeselect =
629
            selectedRowsLen === data.length || (selectedRowsLen < data.length && selectedRowsLen > 0) ? true : false;
630

631
          let selectedRows = Array(data.length)
632
            .fill()
2✔
633
            .map((d, i) => ({ index: i }));
1✔
634

635
          let newRows = [...prevState.selectedRows, ...selectedRows];
1✔
636
          let selectedMap = this.buildSelectedMap(newRows);
1✔
637

638
          if (isDeselect) {
1!
639
            newRows = prevState.selectedRows.data.filter(({ index }) => !selectedMap[index]);
640
            selectedMap = this.buildSelectedMap(newRows);
1✔
641
          }
642

4✔
643
          return {
644
            curSelectedRows: newRows,
1✔
645
            selectedRows: {
1✔
646
              data: newRows,
647
              lookup: selectedMap,
1!
648
            },
×
649
          };
×
650
        },
651
        () => {
652
          if (this.options.onRowsSelect) {
1✔
653
            this.options.onRowsSelect(this.state.curSelectedRows, this.state.selectedRows.data);
654
          }
655
        },
656
      );
657
    } else if (type === "cell") {
658
      this.setState(
659
        prevState => {
660
          const { index, dataIndex } = value;
661
          let selectedRows = [...prevState.selectedRows.data];
1!
662
          let rowPos = -1;
×
663

664
          for (let cIndex = 0; cIndex < selectedRows.length; cIndex++) {
665
            if (selectedRows[cIndex].index === index) {
666
              rowPos = cIndex;
1!
667
              break;
1✔
668
            }
669
          }
1✔
670

1✔
671
          if (rowPos >= 0) {
1✔
672
            selectedRows.splice(rowPos, 1);
673
          } else {
1✔
674
            selectedRows.push(value);
×
675
          }
×
676

×
677
          return {
678
            selectedRows: {
679
              lookup: this.buildSelectedMap(selectedRows),
680
              data: selectedRows,
1!
681
            },
×
682
          };
683
        },
1✔
684
        () => {
685
          if (this.options.onRowsSelect) {
686
            this.options.onRowsSelect([value], this.state.selectedRows.data);
1✔
687
          }
688
        },
689
      );
690
    }
691
  };
692

693
  sortCompare(order) {
694
    return (a, b) => {
1!
695
      if (a.data === null) a.data = "";
×
696
      if (b.data === null) b.data = "";
697
      return (
698
        (typeof a.data.localeCompare === "function" ? a.data.localeCompare(b.data) : a.data - b.data) *
699
        (order === "asc" ? -1 : 1)
700
      );
701
    };
702
  }
703

1✔
704
  sortTable(data, col, order) {
6!
705
    let sortedData = data.map((row, sIndex) => ({
6!
706
      data: row.data[col],
6✔
707
      position: sIndex,
6!
708
      rowSelected: this.state.selectedRows.lookup[sIndex] ? true : false,
6!
709
    }));
710

711
    sortedData.sort(this.sortCompare(order));
712

713
    let tableData = [];
714
    let selectedRows = [];
4✔
715

716
    for (let i = 0; i < sortedData.length; i++) {
717
      const row = sortedData[i];
4!
718
      tableData.push(data[row.position]);
719
      if (row.rowSelected) {
720
        selectedRows.push({ index: i, dataIndex: data[row.position].index });
1✔
721
      }
722
    }
1✔
723

1✔
724
    return {
725
      data: tableData,
1✔
726
      selectedRows: {
4✔
727
        lookup: this.buildSelectedMap(selectedRows),
4✔
728
        data: selectedRows,
4!
729
      },
×
730
    };
731
  }
732

733
  render() {
1✔
734
    const { classes, title } = this.props;
735
    const {
736
      announceText,
737
      activeColumn,
738
      data,
739
      displayData,
740
      columns,
741
      page,
742
      filterData,
743
      filterList,
43✔
744
      rowsPerPage,
745
      selectedRows,
746
      searchText,
747
    } = this.state;
748

749
    if (!data.length) {
750
      return false;
751
    }
752

753
    const rowCount = this.options.count || displayData.length;
754

755
    return (
756
      <Paper elevation={4} ref={el => (this.tableContent = el)} className={classes.paper}>
43✔
757
        {selectedRows.data.length ? (
758
          <MUIDataTableToolbarSelect
43!
759
            options={this.options}
×
760
            selectedRows={selectedRows}
761
            onRowsDelete={this.selectRowDelete}
762
          />
43✔
763
        ) : (
764
          <MUIDataTableToolbar
43✔
765
            columns={columns}
5✔
766
            data={data}
43✔
767
            filterData={filterData}
768
            filterList={filterList}
769
            filterUpdate={this.filterUpdate}
770
            options={this.options}
771
            resetFilters={this.resetFilters}
772
            searchTextUpdate={this.searchTextUpdate}
773
            tableRef={() => this.tableContent}
774
            title={title}
775
            toggleViewColumn={this.toggleViewColumn}
776
          />
777
        )}
778
        <MUIDataTableFilterList options={this.options} filterList={filterList} filterUpdate={this.filterUpdate} />
779
        <div
780
          style={{ position: "relative" }}
781
          className={this.options.responsive === "scroll" ? classes.responsiveScroll : null}>
782
          {this.options.resizableColumns && <MUIDataTableResize setResizeable={fn => (this.setHeadResizeable = fn)} />}
×
783
          <Table ref={el => (this.tableRef = el)} tabIndex={"0"} role={"grid"}>
784
            <caption className={classes.caption}>{title}</caption>
785
            <MUIDataTableHead
786
              columns={columns}
787
              activeColumn={activeColumn}
788
              data={this.state.displayData}
789
              count={rowCount}
790
              columns={columns}
43!
791
              page={page}
×
792
              rowsPerPage={rowsPerPage}
5✔
793
              handleHeadUpdateRef={fn => (this.updateToolbarSelect = fn)}
794
              selectedRows={selectedRows}
795
              selectRowUpdate={this.selectRowUpdate}
796
              toggleSort={this.toggleSortColumn}
797
              setCellRef={this.setHeadCellRef}
798
              options={this.options}
799
            />
800
            <MUIDataTableBody
801
              data={this.state.displayData}
802
              count={rowCount}
3✔
803
              columns={columns}
804
              page={page}
805
              rowsPerPage={rowsPerPage}
806
              selectedRows={selectedRows}
807
              selectRowUpdate={this.selectRowUpdate}
808
              options={this.options}
809
              searchText={searchText}
810
              filterList={filterList}
811
            />
812
          </Table>
813
        </div>
814
        <Table>
815
          {this.options.customFooter
816
            ? this.options.customFooter(rowCount, page, rowsPerPage, this.changeRowsPerPage, this.changePage)
817
            : this.options.pagination && (
818
                <MUIDataTablePagination
819
                  count={rowCount}
820
                  page={page}
821
                  rowsPerPage={rowsPerPage}
822
                  changeRowsPerPage={this.changeRowsPerPage}
823
                  changePage={this.changePage}
824
                  component={"div"}
43!
825
                  options={this.options}
826
                />
85✔
827
              )}
828
        </Table>
829
        <div className={classes.liveAnnounce} aria-live={"polite"} ref={el => (this.announceRef = el)}>
830
          {announceText}
831
        </div>
832
      </Paper>
833
    );
834
  }
835
}
836

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

© 2026 Coveralls, Inc