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

MarkUsProject / Markus / 26595528514

28 May 2026 06:54PM UTC coverage: 90.255% (-0.02%) from 90.277%
26595528514

Pull #7967

github

web-flow
Merge 6642c13e0 into 5b70aab6e
Pull Request #7967: Implement Graders Column in Assignment Grades tab

965 of 2151 branches covered (44.86%)

Branch coverage included in aggregate %.

9 of 10 new or added lines in 1 file covered. (90.0%)

6 existing lines in 2 files now uncovered.

46019 of 49906 relevant lines covered (92.21%)

122.0 hits per line

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

79.52
/app/javascript/Components/table/table.jsx
1
import React from "react";
2
import {Grid} from "react-loader-spinner";
3
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
4

5
import {
6
  createColumnHelper,
7
  getCoreRowModel,
8
  getExpandedRowModel,
9
  getFacetedRowModel,
10
  getFacetedUniqueValues,
11
  getFilteredRowModel,
12
  getSortedRowModel,
13
  useReactTable,
14
} from "@tanstack/react-table";
15
import FilterCell from "./filter";
16
import TableHeaderCell from "./table_header_cell";
17
import TableRow from "./table_row";
18

19
export const defaultNoDataText = () => I18n.t("table.no_data");
15✔
20

21
const columnHelper = createColumnHelper();
7✔
22
export const expanderColumn = columnHelper.display({
7✔
23
  id: "expander",
UNCOV
24
  header: () => null,
×
25
  size: 30,
26
  maxSize: 30,
27
  cell: ({row}) => {
UNCOV
28
    const icon = row.getIsExpanded() ? "fa-chevron-up" : "fa-chevron-down";
×
UNCOV
29
    const title = row.getIsExpanded() ? I18n.t("table.hide_details") : I18n.t("table.show_details");
×
UNCOV
30
    return row.getCanExpand() ? (
×
31
      <div
32
        className={`rt-expandable ${row.getIsExpanded() ? "-open" : ""}`}
×
33
        onClick={row.getToggleExpandedHandler()}
34
        data-testid="expander-button"
35
      >
36
        <FontAwesomeIcon icon={icon} title={title} />
37
      </div>
38
    ) : null;
39
  },
40
});
41

42
export const selectionColumn = columnHelper.display({
7✔
43
  id: "select",
44
  header: ({table}) => {
45
    const checkboxRef = React.useRef(null);
17✔
46

47
    React.useEffect(() => {
17✔
48
      if (checkboxRef.current) {
13!
49
        checkboxRef.current.indeterminate =
13✔
50
          table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected();
13!
51
      }
52
    }, [table.getIsSomeRowsSelected(), table.getIsAllRowsSelected()]);
53

54
    return (
17✔
55
      <input
56
        ref={checkboxRef}
57
        type="checkbox"
58
        checked={table.getIsAllRowsSelected()}
59
        onChange={table.getToggleAllRowsSelectedHandler()}
60
      />
61
    );
62
  },
63
  size: 30,
64
  maxSize: 30,
65
  enableResizing: false,
66
  enableSorting: false,
67
  cell: ({row}) => (
68
    <input
3✔
69
      type="checkbox"
70
      checked={row.getIsSelected()}
71
      disabled={!row.getCanSelect()}
72
      onChange={row.getToggleSelectedHandler()}
73
    />
74
  ),
75
});
76

77
export default function Table({
78
  columns,
79
  data,
80
  noDataText,
81
  initialState,
82
  loading,
83
  renderSubComponent,
84
  getRowCanExpand,
85
  getRowId,
86
  enableRowSelection,
87
  rowSelection: externalRowSelection,
88
  columnFilters: externalColumnFilters,
89
  onColumnFiltersChange: externalOnColumnFiltersChange,
90
  onRowSelectionChange,
91
}) {
92
  const [internalColumnFilters, setInternalColumnFilters] = React.useState([]);
166✔
93
  const [columnSizing, setColumnSizing] = React.useState({});
166✔
94
  const [columnVisibility, setColumnVisibility] = React.useState({
166✔
95
    inactive: false,
96
    ...initialState?.columnVisibility,
97
  });
98
  const [expanded, setExpanded] = React.useState({});
166✔
99
  const [internalRowSelection, setInternalRowSelection] = React.useState({});
166✔
100

101
  const columnFilters = React.useMemo(
166✔
102
    () => (externalColumnFilters !== undefined ? externalColumnFilters : internalColumnFilters),
84✔
103
    [externalColumnFilters, internalColumnFilters]
104
  );
105

106
  const handleColumnFiltersChange = React.useMemo(
166✔
107
    () =>
108
      externalOnColumnFiltersChange !== undefined
86✔
109
        ? externalOnColumnFiltersChange
110
        : setInternalColumnFilters,
111
    [externalOnColumnFiltersChange]
112
  );
113

114
  const rowSelection = React.useMemo(
166✔
115
    () => (externalRowSelection !== undefined ? externalRowSelection : internalRowSelection),
59✔
116
    [externalRowSelection, internalRowSelection]
117
  );
118

119
  const handleRowSelectionChange = React.useMemo(
166✔
120
    () => (onRowSelectionChange !== undefined ? onRowSelectionChange : setInternalRowSelection),
64✔
121
    [onRowSelectionChange]
122
  );
123

124
  const finalColumns = React.useMemo(() => {
166✔
125
    let cols = [...columns];
75✔
126
    if (enableRowSelection) {
75✔
127
      cols = [selectionColumn, ...cols];
17✔
128
    }
129
    if (renderSubComponent) {
75!
UNCOV
130
      cols = [expanderColumn, ...cols];
×
131
    }
132
    return cols;
75✔
133
  }, [columns, enableRowSelection, renderSubComponent]);
134

135
  const table = useReactTable({
166✔
136
    data,
137
    columns: finalColumns,
138
    state: {
139
      columnFilters,
140
      columnSizing,
141
      columnVisibility,
142
      expanded,
143
      rowSelection,
144
    },
145
    initialState: initialState,
146
    onColumnFiltersChange: handleColumnFiltersChange,
147
    onColumnSizingChange: setColumnSizing,
148
    onColumnVisibilityChange: setColumnVisibility,
149
    onExpandedChange: setExpanded,
150
    onRowSelectionChange: handleRowSelectionChange,
151
    getCoreRowModel: getCoreRowModel(),
152
    getExpandedRowModel: getExpandedRowModel(),
153
    getFilteredRowModel: getFilteredRowModel(),
154
    getSortedRowModel: getSortedRowModel(),
155
    getFacetedUniqueValues: getFacetedUniqueValues(),
156
    getFacetedRowModel: getFacetedRowModel(),
157
    getRowCanExpand,
158
    getRowId,
159
    enableSortingRemoval: false,
160
    enableColumnResizing: true,
161
    enableRowSelection: enableRowSelection,
162
    columnResizeMode: "onChange",
163
  });
164

165
  const centerTotalSize = table.getCenterTotalSize();
166✔
166

167
  const tableHeaders = (
168
    <div className="rt-thead -header" style={{minWidth: centerTotalSize}}>
166✔
169
      {table.getHeaderGroups().map(headerGroup => (
170
        <div className="rt-tr" role="row" key={headerGroup.id}>
166✔
171
          {headerGroup.headers.map(header => (
172
            <TableHeaderCell
925✔
173
              key={header.id}
174
              header={header}
175
              size={header.getSize()}
176
              isSorted={header.column.getIsSorted()}
177
              isResizing={header.column.getIsResizing()}
178
            />
179
          ))}
180
        </div>
181
      ))}
182
    </div>
183
  );
184

185
  const showFilters = React.useMemo(
166✔
186
    () => table.getAllColumns().some(column => column.getCanFilter()),
92✔
187
    [table, finalColumns]
188
  );
189
  const tableFilters = showFilters && (
166✔
190
    <div className="rt-thead -filters" style={{minWidth: centerTotalSize}}>
191
      {table.getHeaderGroups().map(headerGroup => (
192
        <div className="rt-tr" role="row" key={headerGroup.id}>
166✔
193
          {headerGroup.headers.map(header => (
194
            <FilterCell
925✔
195
              key={header.id}
196
              size={header.getSize()}
197
              column={header.column}
198
              filterValue={header.column.getFilterValue()}
199
              facetedUniqueValues={
200
                header.column.columnDef.meta?.filterVariant === "select"
925✔
201
                  ? header.column.getFacetedUniqueValues()
202
                  : null
203
              }
204
            />
205
          ))}
206
        </div>
207
      ))}
208
    </div>
209
  );
210

211
  return (
166✔
212
    <div className="Table -highlight" style={{maxHeight: "500px"}}>
213
      <div className="rt-table" role="grid">
214
        {tableHeaders}
215
        {tableFilters}
216
        <div className="rt-tbody" style={{minWidth: centerTotalSize}}>
217
          {table.getRowModel().rows.map(row => (
218
            <TableRow
215✔
219
              row={row}
220
              isExpanded={row.getIsExpanded()}
221
              isSelected={row.getIsSelected()}
222
              key={row.id}
223
              renderSubComponent={renderSubComponent}
224
              // columnSizing is not used directly in TableRow, but is passed to trigger
225
              // re-render when column sizes change
226
              columnSizing={columnSizing}
227
            />
228
          ))}
229
          {loading && table.getRowModel().rows.length > 0 && (
213!
230
            <div
231
              className="loading-spinner"
232
              style={{
233
                position: "absolute",
234
                bottom: 0,
235
                left: 0,
236
                right: 0,
237
                height: "auto",
238
                backgroundColor: "#fff",
239
                zIndex: 10,
240
                border: "solid 1px rgba(0, 0, 0, 0.05)",
241
              }}
242
            >
243
              <Grid
244
                visible={true}
245
                height="25"
246
                width="25"
247
                color="#31649B"
248
                ariaLabel="grid-loading"
249
                radius="12.5"
250
                wrapperStyle={{}}
251
                wrapperClass="grid-wrapper"
252
              />
253
            </div>
254
          )}
255
          {!table.getRowModel().rows.length &&
236✔
256
            (loading ? (
70✔
257
              <div className="loading-spinner">
258
                <Grid
259
                  visible={true}
260
                  height="25"
261
                  width="25"
262
                  color="#31649B"
263
                  ariaLabel="grid-loading"
264
                  radius="12.5"
265
                  wrapperStyle={{}}
266
                  wrapperClass="grid-wrapper"
267
                />
268
              </div>
269
            ) : (
270
              <p className="rt-no-data">{noDataText || defaultNoDataText()}</p>
37✔
271
            ))}
272
        </div>
273
      </div>
274
    </div>
275
  );
276
}
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