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

MarkUsProject / Markus / 16631415652

30 Jul 2025 07:03PM UTC coverage: 91.885% (+0.003%) from 91.882%
16631415652

Pull #7602

github

web-flow
Merge 029952563 into aa5a99c03
Pull Request #7602: Added loading icon for instructor table

662 of 1423 branches covered (46.52%)

Branch coverage included in aggregate %.

2 of 16 new or added lines in 2 files covered. (12.5%)

10 existing lines in 3 files now uncovered.

42036 of 45046 relevant lines covered (93.32%)

118.71 hits per line

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

18.06
/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.
NEW
12
  if ((a === undefined || a === null) && (b === undefined || b === null)) {
×
NEW
13
    return 0;
×
NEW
14
  } else if (a === undefined || a === null) {
×
NEW
15
    return -1;
×
NEW
16
  } else if (b === undefined || b === null) {
×
NEW
17
    return 1;
×
18
  } else {
19
    // force any string values to lowercase
NEW
20
    a = typeof a === "string" ? a.toLowerCase() : a;
×
NEW
21
    b = typeof b === "string" ? b.toLowerCase() : b;
×
22
    // Return either 1 or -1 to indicate a sort priority
NEW
23
    if (a > b) {
×
NEW
24
      return 1;
×
25
    }
NEW
26
    if (a < b) {
×
NEW
27
      return -1;
×
28
    }
29
    // returning 0, undefined or any falsey value will use subsequent sorts or
30
    // the index as a tiebreaker
NEW
31
    return 0;
×
32
  }
33
}
34

35
/**
36
 * Case insensitive, locale aware, string filter function
37
 */
38
export function stringFilterMethod(filter, row) {
NEW
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}) => (
134!
99
    <option value={value} key={value}>
345✔
100
      {text}
101
    </option>
102
  ));
103
  let allOptionText = column.filterAllOptionText || I18n.t("all");
134✔
104
  options.unshift(
134✔
105
    <option value="all" key="all">
106
      {allOptionText}
107
    </option>
108
  );
109

110
  return (
134✔
111
    <select
112
      onChange={event => onChange(event.target.value)}
×
113
      style={{width: "100%"}}
114
      value={filter ? filter.value : "all"}
134!
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 {
26✔
124
    Header: I18n.t("activerecord.attributes.result.marking_state"),
125
    accessor: "marking_state",
126
    Cell: row => {
127
      let marking_state = "";
26✔
128
      switch (row.original.marking_state) {
26!
129
        case "not_collected":
130
          marking_state = I18n.t("submissions.state.not_collected");
×
131
          break;
×
132
        case "incomplete":
133
          marking_state = I18n.t("submissions.state.in_progress");
×
134
          break;
×
135
        case "complete":
136
          marking_state = I18n.t("submissions.state.complete");
×
137
          break;
×
138
        case "released":
139
          marking_state = I18n.t("submissions.state.released");
26✔
140
          break;
26✔
141
        case "remark":
142
          marking_state = I18n.t("submissions.state.remark_requested");
×
143
          break;
×
144
        case "before_due_date":
145
          marking_state = I18n.t("submissions.state.before_due_date");
×
146
          break;
×
147
        default:
148
          // should not get here
149
          marking_state = row.original.marking_state;
×
150
      }
151
      return marking_state;
26✔
152
    },
153
    filterMethod: (filter, row) => {
154
      if (filter.value === "all") {
×
155
        return true;
×
156
      } else {
157
        return filter.value === row[filter.id];
×
158
      }
159
    },
160
    filterAllOptionText:
161
      I18n.t("all") +
162
      (markingStateFilter === "all"
26!
163
        ? ` (${Object.values(marking_states).reduce((a, b) => a + b)})`
130✔
164
        : ""),
165
    filterOptions: [
166
      {
167
        value: "before_due_date",
168
        text:
169
          I18n.t("submissions.state.before_due_date") +
170
          (["before_due_date", "all"].includes(markingStateFilter)
26!
171
            ? ` (${marking_states["before_due_date"]})`
172
            : ""),
173
      },
174
      {
175
        value: "not_collected",
176
        text:
177
          I18n.t("submissions.state.not_collected") +
178
          (["not_collected", "all"].includes(markingStateFilter)
26!
179
            ? ` (${marking_states["not_collected"]})`
180
            : ""),
181
      },
182
      {
183
        value: "incomplete",
184
        text:
185
          I18n.t("submissions.state.in_progress") +
186
          (["incomplete", "all"].includes(markingStateFilter)
26!
187
            ? ` (${marking_states["incomplete"]})`
188
            : ""),
189
      },
190
      {
191
        value: "complete",
192
        text:
193
          I18n.t("submissions.state.complete") +
194
          (["complete", "all"].includes(markingStateFilter)
26!
195
            ? ` (${marking_states["complete"]})`
196
            : ""),
197
      },
198
      {
199
        value: "released",
200
        text:
201
          I18n.t("submissions.state.released") +
202
          (["released", "all"].includes(markingStateFilter)
26!
203
            ? ` (${marking_states["released"]})`
204
            : ""),
205
      },
206
      {
207
        value: "remark",
208
        text:
209
          I18n.t("submissions.state.remark_requested") +
210
          (["remark", "all"].includes(markingStateFilter) ? ` (${marking_states["remark"]})` : ""),
26!
211
      },
212
    ],
213
    Filter: selectFilter,
214
    ...override_keys,
215
  };
216
}
217

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

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