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

MarkUsProject / Markus / 28673864361

03 Jul 2026 04:57PM UTC coverage: 90.285% (+0.01%) from 90.274%
28673864361

Pull #8021

github

web-flow
Merge b4eadb135 into 74cd8944e
Pull Request #8021: Migrate annotation_usage_panel.jsx to react table v8

1194 of 2444 branches covered (48.85%)

Branch coverage included in aggregate %.

17 of 18 new or added lines in 2 files covered. (94.44%)

1 existing line in 1 file now uncovered.

47078 of 51022 relevant lines covered (92.27%)

127.14 hits per line

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

77.36
/app/javascript/Components/annotation_usage_panel.jsx
1
import React from "react";
2
import {createRoot} from "react-dom/client";
3
import {createColumnHelper} from "@tanstack/react-table";
4
import Table from "./table/table";
5

6
import {caseSensitiveIncludes} from "./Helpers/table_helpers";
7

8
const columnHelper = createColumnHelper();
1✔
9
class AnnotationUsagePanel extends React.Component {
10
  constructor(props) {
11
    super(props);
3✔
12
    this.state = {
2✔
13
      applications: null,
14
      columnFilters: [],
15
      details: false,
16
    };
17
  }
18

19
  toggle = () => {
2✔
20
    if (this.state.applications === null) {
2!
21
      this.fetchData();
2✔
22
    } else {
23
      this.setState({details: !this.state.details});
×
24
    }
25
  };
26

27
  columns = [
2✔
28
    columnHelper.accessor(
29
      row => "(" + row.user_name + ") " + row.first_name + " " + row.last_name,
6✔
30
      {
31
        header: I18n.t("annotations.used_by"),
32
        id: "user",
33
        minSize: 200,
34
        cell: ({getValue, row}) => {
35
          if (row.getIsGrouped()) {
6!
36
            return getValue();
6✔
37
          }
NEW
38
          return null;
×
39
        },
40
      }
41
    ),
42
    columnHelper.accessor("group_name", {
43
      header: I18n.t("activerecord.models.submission.one"),
44
      enableSorting: false,
45
      aggregationFn: (columnId, leafRows, childRows) => {
46
        return leafRows.reduce((acc, row) => acc + row.original.count, 0);
6✔
47
      },
48
      cell: props => {
49
        if (props.row.getIsGrouped()) {
6!
50
          return I18n.t("annotations.used_times", {
6✔
51
            count: props.row.getValue("group_name"),
52
          });
53
        }
54

UNCOV
55
        return (
×
56
          <a
57
            href={Routes.edit_course_result_path(
58
              this.props.course_id,
59
              props.row.original.result_id
60
            )}
61
          >
62
            {props.row.original.group_name +
63
              (props.row.original.count > 1 ? ` (${props.row.original.count})` : "")}
×
64
          </a>
65
        );
66
      },
67
      filterFn: (row, columnId, filterValue) => {
68
        const value = filterValue?.value;
9✔
69
        const caseSensitive = filterValue?.caseSensitive;
9✔
70

71
        if (!value) return true;
9✔
72

73
        return caseSensitiveIncludes(row.original[columnId], value, caseSensitive);
6✔
74
      },
75
      meta: {
76
        filterVariant: "case-sensitive-text",
77
      },
78
    }),
79
  ];
80

81
  fetchData = () => {
2✔
82
    const url = Routes.annotation_text_uses_course_assignment_annotation_categories_path(
2✔
83
      this.props.course_id,
84
      this.props.assignment_id,
85
      {
86
        annotation_text_id: this.props.annotation_id,
87
      }
88
    );
89
    fetch(url, {
2✔
90
      headers: {
91
        Accept: "application/json",
92
      },
93
    })
94
      .then(response => {
95
        if (response.ok) {
2!
96
          return response.json();
2✔
97
        }
98
      })
99
      .then(res => {
100
        this.setState({applications: res, details: true});
2✔
101
      });
102
  };
103

104
  render() {
105
    let numUsed = <p>{I18n.t("annotations.count") + this.props.num_used}</p>;
7✔
106
    let displayToggle = (
107
      <p>
7✔
108
        <a onClick={this.toggle} className="button">
109
          {I18n.t("annotations.usage")}
110
        </a>
111
      </p>
112
    );
113
    if (this.state.details) {
7✔
114
      let annotation_table = (
115
        <Table
5✔
116
          data={this.state.applications}
117
          columns={this.columns}
118
          initialState={{
119
            grouping: ["user"],
120
          }}
121
          columnFilters={this.state.columnFilters}
122
          onColumnFiltersChange={updaterOrValue => {
123
            this.setState(prevState => {
3✔
124
              let newFilters =
125
                typeof updaterOrValue === "function"
3!
126
                  ? updaterOrValue(prevState.columnFilters)
127
                  : updaterOrValue;
128
              return {columnFilters: newFilters};
3✔
129
            });
130
          }}
131
          renderSubRows={true}
132
        />
133
      );
134
      return (
5✔
135
        <fieldset>
136
          {numUsed}
137
          {displayToggle}
138
          {annotation_table}
139
        </fieldset>
140
      );
141
    } else {
142
      return (
2✔
143
        <fieldset>
144
          {numUsed}
145
          {displayToggle}
146
        </fieldset>
147
      );
148
    }
149
  }
150
}
151

152
export function makeAnnotationUsagePanel(elem, props) {
153
  const root = createRoot(elem);
×
154
  return root.render(<AnnotationUsagePanel {...props} />);
×
155
}
156

157
export {AnnotationUsagePanel};
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