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

MarkUsProject / Markus / 16927327079

13 Aug 2025 04:14AM UTC coverage: 91.928% (+0.04%) from 91.884%
16927327079

Pull #7630

github

web-flow
Merge cc329a3f0 into 50db86a75
Pull Request #7630: Migrate assignment summary page to react-table v8

667 of 1421 branches covered (46.94%)

Branch coverage included in aggregate %.

43 of 51 new or added lines in 3 files covered. (84.31%)

73 existing lines in 6 files now uncovered.

42111 of 45113 relevant lines covered (93.35%)

118.5 hits per line

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

18.67
/app/javascript/Components/Helpers/table_helpers.jsx
1
import React from "react";
2

3
/**
4
 * @file
5
 * Provides generic helper functions and components for react-table tables.
6
 */
7

8
export function defaultSort(a, b) {
9
  // Sort values, putting undefined/nulls below all other values.
10
  // Based on react-table v6 defaultSortMethod (https://github.com/tannerlinsley/react-table/tree/v6/),
11
  // but not string-based.
12
  if ((a === undefined || a === null) && (b === undefined || b === null)) {
×
13
    return 0;
×
14
  } else if (a === undefined || a === null) {
×
15
    return -1;
×
16
  } else if (b === undefined || b === null) {
×
17
    return 1;
×
18
  } else {
19
    // force any string values to lowercase
20
    a = typeof a === "string" ? a.toLowerCase() : a;
×
21
    b = typeof b === "string" ? b.toLowerCase() : b;
×
22
    // Return either 1 or -1 to indicate a sort priority
23
    if (a > b) {
×
24
      return 1;
×
25
    }
26
    if (a < b) {
×
27
      return -1;
×
28
    }
29
    // returning 0, undefined or any falsey value will use subsequent sorts or
30
    // the index as a tiebreaker
31
    return 0;
×
32
  }
33
}
34

35
/**
36
 * Case insensitive, locale aware, string filter function
37
 */
38
export function stringFilterMethod(filter, row) {
39
  return String(row[filter.id])
×
40
    .toLocaleLowerCase()
41
    .includes(String(filter.value).toLocaleLowerCase());
42
}
43

44
export function dateSort(a, b) {
45
  /** Sort values as dates */
46
  if (!a && !b) {
×
47
    return 0;
×
48
  } else if (!a) {
×
49
    return -1;
×
50
  } else if (!b) {
×
51
    return 1;
×
52
  } else {
53
    let a_date = new Date(a);
×
54
    let b_date = new Date(b);
×
55
    return (a_date || 0) - (b_date || 0);
×
56
  }
57
}
58

59
export function durationSort(a, b) {
60
  /** Sort values as a duration in weeks, days, hours, etc. */
61
  a = [a.weeks || -1, a.days || -1, a.hours || -1, a.minutes || -1, a.seconds || -1];
×
62
  b = [b.weeks || -1, b.days || -1, b.hours || -1, b.minutes || -1, b.seconds || -1];
×
63
  if (a < b) {
×
64
    return 1;
×
65
  } else if (b < a) {
×
66
    return -1;
×
67
  } else {
68
    return 0;
×
69
  }
70
}
71

72
/**
73
 * Text-based search filter. Based on react-table's default search filter,
74
 * with an additional aria-label attribute.
75
 */
76
export function textFilter({filter, onChange, column}) {
77
  return (
×
78
    <input
79
      type="text"
80
      style={{
81
        width: "100%",
82
      }}
83
      placeholder={column.Placeholder}
84
      value={filter ? filter.value : ""}
×
85
      aria-label={`${I18n.t("search")} ${column.Header || ""}`}
×
86
      onChange={event => onChange(event.target.value)}
×
87
    />
88
  );
89
}
90

91
/**
92
 * Select-based search filter. Options are generated from the custom column attribute
93
 * filterOptions, which is a list of objects with keys "value" and "text".
94
 * A default "all" option is prepended to the list of options; the text can be
95
 * customized by setting the filterAllOptionText column attribute.
96
 */
97
export function selectFilter({filter, onChange, column}) {
98
  let options = (column.filterOptions || []).map(({value, text}) => (
110!
99
    <option value={value} key={value}>
201✔
100
      {text}
101
    </option>
102
  ));
103
  let allOptionText = column.filterAllOptionText || I18n.t("all");
110✔
104
  options.unshift(
110✔
105
    <option value="all" key="all">
106
      {allOptionText}
107
    </option>
108
  );
109

110
  return (
110✔
111
    <select
112
      onChange={event => onChange(event.target.value)}
×
113
      style={{width: "100%"}}
114
      value={filter ? filter.value : "all"}
110!
115
      aria-label={I18n.t("filter_by", {name: column.Header})}
116
    >
117
      {options}
118
    </select>
119
  );
120
}
121

122
export function markingStateColumn(marking_states, markingStateFilter, ...override_keys) {
123
  return {
30✔
124
    header: I18n.t("activerecord.attributes.result.marking_state"),
125
    accessorKey: "marking_state",
126
    size: 100,
127
    enableResizing: true,
128
    cell: info => {
129
      let marking_state = "";
22✔
130
      switch (info.row.original.marking_state) {
22!
131
        case "not_collected":
132
          marking_state = I18n.t("submissions.state.not_collected");
×
133
          break;
×
134
        case "incomplete":
135
          marking_state = I18n.t("submissions.state.in_progress");
×
136
          break;
×
137
        case "complete":
138
          marking_state = I18n.t("submissions.state.complete");
×
139
          break;
×
140
        case "released":
141
          marking_state = I18n.t("submissions.state.released");
22✔
142
          break;
22✔
143
        case "remark":
144
          marking_state = I18n.t("submissions.state.remark_requested");
×
145
          break;
×
146
        case "before_due_date":
147
          marking_state = I18n.t("submissions.state.before_due_date");
×
148
          break;
×
149
        default:
150
          // should not get here
NEW
151
          marking_state = info.row.original.marking_state;
×
152
      }
153
      return marking_state;
22✔
154
    },
155
    filterFn: "equalsString",
156
    meta: {
157
      filterVariant: "select",
158
    },
159
    filterAllOptionText:
160
      I18n.t("all") +
161
      (markingStateFilter === "all"
30!
162
        ? ` (${Object.values(marking_states).reduce((a, b) => a + b)})`
150✔
163
        : ""),
164
    filterOptions: [
165
      {
166
        value: "before_due_date",
167
        text:
168
          I18n.t("submissions.state.before_due_date") +
169
          (["before_due_date", "all"].includes(markingStateFilter)
30!
170
            ? ` (${marking_states["before_due_date"]})`
171
            : ""),
172
      },
173
      {
174
        value: "not_collected",
175
        text:
176
          I18n.t("submissions.state.not_collected") +
177
          (["not_collected", "all"].includes(markingStateFilter)
30!
178
            ? ` (${marking_states["not_collected"]})`
179
            : ""),
180
      },
181
      {
182
        value: "incomplete",
183
        text:
184
          I18n.t("submissions.state.in_progress") +
185
          (["incomplete", "all"].includes(markingStateFilter)
30!
186
            ? ` (${marking_states["incomplete"]})`
187
            : ""),
188
      },
189
      {
190
        value: "complete",
191
        text:
192
          I18n.t("submissions.state.complete") +
193
          (["complete", "all"].includes(markingStateFilter)
30!
194
            ? ` (${marking_states["complete"]})`
195
            : ""),
196
      },
197
      {
198
        value: "released",
199
        text:
200
          I18n.t("submissions.state.released") +
201
          (["released", "all"].includes(markingStateFilter)
30!
202
            ? ` (${marking_states["released"]})`
203
            : ""),
204
      },
205
      {
206
        value: "remark",
207
        text:
208
          I18n.t("submissions.state.remark_requested") +
209
          (["remark", "all"].includes(markingStateFilter) ? ` (${marking_states["remark"]})` : ""),
30!
210
      },
211
    ],
212
    Filter: selectFilter,
213
    ...override_keys,
214
  };
215
}
216

217
export function getMarkingStates(data) {
218
  const markingStates = {
24✔
219
    not_collected: 0,
220
    incomplete: 0,
221
    complete: 0,
222
    released: 0,
223
    remark: 0,
224
    before_due_date: 0,
225
  };
226
  data.forEach(row => {
24✔
227
    markingStates[row["marking_state"]] += 1;
20✔
228
  });
229
  return markingStates;
24✔
230
}
231

232
export function customNoDataComponent({children}) {
233
  return <p className="rt-no-data">{children}</p>;
×
234
}
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