Skip to content

Commit

Permalink
Refactor Result components to move identifying info into Context (Mar…
Browse files Browse the repository at this point in the history
  • Loading branch information
ch-iv authored and soheegoo committed Jan 27, 2025
1 parent 5eb1738 commit 4143392
Show file tree
Hide file tree
Showing 19 changed files with 294 additions and 262 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- Fix `extra_hosts` configuration in `compose.yaml` (#7375)
- Add unit tests for `marks_graders_controller` (#7382)
- Convert front-end tests from enzyme to react testing library; add `@testing-library/user-event` (#7379)
- Refactor the `Result` component and its children to use React context API (#7380)

## [v2.6.1]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import Modal from "react-modal";

import MarkdownEditor from "../markdown_editor";
import {ResultContext} from "../Result/result_context";

class CreateModifyAnnotationPanel extends React.Component {
constructor(props) {
Expand All @@ -16,6 +17,8 @@ class CreateModifyAnnotationPanel extends React.Component {
};
}

static contextType = ResultContext;

updateAnnotationCompletion = () => {
const annotationTextList = document.getElementById("annotation_text_list");
const value = this.state.content;
Expand All @@ -30,8 +33,8 @@ class CreateModifyAnnotationPanel extends React.Component {
}

const url = Routes.find_annotation_text_course_assignment_annotation_categories_path(
this.props.course_id,
this.props.assignment_id,
this.context.course_id,
this.context.assignment_id,
{string: value}
);

Expand Down Expand Up @@ -93,7 +96,7 @@ class CreateModifyAnnotationPanel extends React.Component {
}
});

if (!this.props.is_reviewer) {
if (!this.context.is_reviewer) {
textArea.keyup(e => {
if (typing_timer) {
clearTimeout(typing_timer);
Expand Down Expand Up @@ -222,7 +225,7 @@ class CreateModifyAnnotationPanel extends React.Component {
name="annotation_text_id"
value={this.props.annotation_text_id}
/>
{this.props.is_reviewer ? (
{this.context.is_reviewer ? (
<input
type="hidden"
id="new_annotation_category"
Expand Down
10 changes: 5 additions & 5 deletions app/javascript/Components/Modals/create_tag_modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import TagModal from "../Helpers/tag_modal";
import Modal from "react-modal";
import PropTypes from "prop-types";
import {ResultContext} from "../Result/result_context";

export default class CreateTagModal extends React.Component {
constructor(props) {
Expand All @@ -12,6 +13,8 @@ export default class CreateTagModal extends React.Component {
};
}

static contextType = ResultContext;

componentDidMount() {
Modal.setAppElement("body");
}
Expand All @@ -23,7 +26,7 @@ export default class CreateTagModal extends React.Component {
name: this.state.name,
description: this.state.description,
},
grouping_id: this.props.grouping_id,
grouping_id: this.context.grouping_id,
};
const options = {
method: "POST",
Expand All @@ -34,7 +37,7 @@ export default class CreateTagModal extends React.Component {
body: JSON.stringify(data),
};
fetch(
Routes.course_tags_path(this.props.course_id, {assignment_id: this.props.assignment_id}),
Routes.course_tags_path(this.context.course_id, {assignment_id: this.context.assignment_id}),
options
)
.catch(error => {
Expand Down Expand Up @@ -74,7 +77,4 @@ export default class CreateTagModal extends React.Component {
CreateTagModal.propType = {
isOpen: PropTypes.bool.isRequired,
onRequestClose: PropTypes.func.isRequired,
grouping_id: PropTypes.number,
course_id: PropTypes.number.isRequired,
assignment_id: PropTypes.number.isRequired,
};
5 changes: 4 additions & 1 deletion app/javascript/Components/Modals/filter_modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import {MultiSelectDropdown} from "../DropDown/MultiSelectDropDown";
import {SingleSelectDropDown} from "../DropDown/SingleSelectDropDown";
import {CriteriaFilter} from "../criteria_filter";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {ResultContext} from "../Result/result_context";

export class FilterModal extends React.Component {
static contextType = ResultContext;

onToggleOptionTas = user_name => {
const newArray = [...this.props.filterData.tas];
if (newArray.includes(user_name)) {
Expand Down Expand Up @@ -47,7 +50,7 @@ export class FilterModal extends React.Component {
};

renderTasDropdown = () => {
if (this.props.role === "Instructor") {
if (this.context.role === "Instructor") {
let tas = this.props.tas.map(option => {
return {key: option[0], display: option[0] + " - " + option[1]};
});
Expand Down
9 changes: 6 additions & 3 deletions app/javascript/Components/Result/feedback_file_panel.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from "react";
import {FileViewer} from "./file_viewer";
import mime from "mime/lite";
import {ResultContext} from "./result_context";

export class FeedbackFilePanel extends React.Component {
static contextType = ResultContext;

constructor(props) {
super(props);
if (props.loading) {
Expand Down Expand Up @@ -65,7 +68,7 @@ export class FeedbackFilePanel extends React.Component {
</a>
);
if (this.state.selectedFile !== null) {
url = Routes.course_feedback_file_path(this.props.course_id, this.state.selectedFile);
url = Routes.course_feedback_file_path(this.context.course_id, this.state.selectedFile);
file_obj = this.props.feedbackFiles.find(file => file.id === this.state.selectedFile);
download_feedback_file = (
<a className="button" href={url} download>
Expand All @@ -86,8 +89,8 @@ export class FeedbackFilePanel extends React.Component {
key={"feedback-file-view"}
>
<FileViewer
assignment_id={this.props.assignment_id}
submission_id={this.props.submission_id}
assignment_id={this.context.assignment_id}
submission_id={this.context.submission_id}
selectedFile={file_obj.filename}
selectedFileURL={url}
mime_type={mime.getType(file_obj.filename)}
Expand Down
87 changes: 42 additions & 45 deletions app/javascript/Components/Result/left_pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {FeedbackFilePanel} from "./feedback_file_panel";
import {RemarkPanel} from "./remark_panel";
import {SubmissionFilePanel} from "./submission_file_panel";
import {TestRunTable} from "../test_run_table";
import {ResultContext} from "./result_context";

export class LeftPane extends React.Component {
constructor(props) {
Expand All @@ -17,35 +18,37 @@ export class LeftPane extends React.Component {
this.submissionFilePanel = React.createRef();
}

static getDerivedStateFromProps(props, state) {
static contextType = ResultContext;

getSelectedTabIndex() {
// Reset selected tab state if props are changed and a tab becomes disabled.
// Affected tabs are "Test Results" (tabIndex 2), "Feedback Files" (tabIndex 3),
// and "Remark Request" (tabIndex 4).
if (
(state.tabIndex === 2 && LeftPane.disableTestResultsPanel(props)) ||
(state.tabIndex === 3 && LeftPane.disableFeedbackFilesPanel(props)) ||
(state.tabIndex === 4 && LeftPane.disableRemarkPanel(props))
(this.state.tabIndex === 2 && this.disableTestResultsPanel()) ||
(this.state.tabIndex === 3 && this.disableFeedbackFilesPanel()) ||
(this.state.tabIndex === 4 && this.disableRemarkPanel())
) {
return {tabIndex: 0};
return 0;
} else {
return null;
return this.state.tabIndex;
}
}

static disableTestResultsPanel(props) {
return props.is_reviewer || !props.enable_test;
disableTestResultsPanel() {
return this.context.is_reviewer || !this.props.enable_test;
}

static disableFeedbackFilesPanel(props) {
return props.is_reviewer || props.feedback_files.length === 0;
disableFeedbackFilesPanel() {
return this.context.is_reviewer || this.props.feedback_files.length === 0;
}

static disableRemarkPanel(props) {
if (props.is_reviewer || !props.allow_remarks) {
disableRemarkPanel() {
if (this.context.is_reviewer || !this.props.allow_remarks) {
return true;
} else if (props.student_view) {
} else if (this.props.student_view) {
return false;
} else if (props.remark_submitted) {
} else if (this.props.remark_submitted) {
return false;
} else {
return true;
Expand All @@ -65,46 +68,48 @@ export class LeftPane extends React.Component {

render() {
return (
<Tabs selectedIndex={this.state.tabIndex} onSelect={tabIndex => this.setState({tabIndex})}>
<Tabs
selectedIndex={this.getSelectedTabIndex()}
onSelect={tabIndex => this.setState({tabIndex})}
>
<TabList>
<Tab>{I18n.t("activerecord.attributes.submission.submission_files")}</Tab>
<Tab>{I18n.t("activerecord.models.annotation.other")}</Tab>
<Tab disabled={LeftPane.disableTestResultsPanel(this.props)}>
<Tab disabled={this.disableTestResultsPanel()}>
{I18n.t("activerecord.models.test_result.other")}
</Tab>
<Tab disabled={LeftPane.disableFeedbackFilesPanel(this.props)}>
<Tab disabled={this.disableFeedbackFilesPanel()}>
{I18n.t("activerecord.attributes.submission.feedback_files")}
</Tab>
<Tab disabled={LeftPane.disableRemarkPanel(this.props)}>
<Tab disabled={this.disableRemarkPanel()}>
{I18n.t("activerecord.attributes.submission.submitted_remark")}
</Tab>
</TabList>
<TabPanel forceRender={true}>
<SubmissionFilePanel
ref={this.submissionFilePanel}
result_id={this.props.result_id}
submission_id={this.props.submission_id}
assignment_id={this.props.assignment_id}
grouping_id={this.props.grouping_id}
result_id={this.context.result_id}
submission_id={this.context.submission_id}
assignment_id={this.context.assignment_id}
grouping_id={this.context.grouping_id}
revision_identifier={this.props.revision_identifier}
show_annotation_manager={!this.props.released_to_students}
canDownload={this.props.is_reviewer === undefined ? undefined : !this.props.is_reviewer}
canDownload={
this.context.is_reviewer === undefined ? undefined : !this.context.is_reviewer
}
fileData={this.props.submission_files}
annotation_categories={this.props.annotation_categories}
annotations={this.props.annotations}
newAnnotation={this.props.newAnnotation}
addExistingAnnotation={this.props.addExistingAnnotation}
released_to_students={this.props.released_to_students}
loading={this.props.loading}
course_id={this.props.course_id}
course_id={this.context.course_id}
/>
</TabPanel>
<TabPanel forceRender={true}>
<div id="annotations_summary">
<AnnotationPanel
result_id={this.props.result_id}
submission_id={this.props.submission_id}
assignment_id={this.props.assignment_id}
detailed={this.props.detailed_annotations}
released_to_students={this.props.released_to_students}
overallComment={this.props.overall_comment || ""}
Expand All @@ -114,11 +119,10 @@ export class LeftPane extends React.Component {
editAnnotation={this.props.editAnnotation}
removeAnnotation={this.props.removeAnnotation}
selectFile={this.selectFile}
course_id={this.props.course_id}
/>
</div>
</TabPanel>
<TabPanel forceRender={!LeftPane.disableTestResultsPanel(this.props)}>
<TabPanel forceRender={!this.disableTestResultsPanel()}>
<div id="testviewer">
{/* student results page (with instructor tests released) does not need the button */}
{!this.props.student_view && (
Expand All @@ -127,8 +131,8 @@ export class LeftPane extends React.Component {
method="post"
data-remote="true"
action={Routes.run_tests_course_result_path(
this.props.course_id,
this.props.result_id
this.context.course_id,
this.context.result_id
)}
>
<button type="submit" disabled={!this.props.can_run_tests}>
Expand All @@ -141,30 +145,25 @@ export class LeftPane extends React.Component {
)}

<TestRunTable
result_id={this.props.result_id}
submission_id={this.props.submission_id}
assignment_id={this.props.assignment_id}
grouping_id={this.props.grouping_id}
result_id={this.context.result_id}
course_id={this.context.course_id}
assignment_id={this.context.assignment_id}
grouping_id={this.context.grouping_id}
submission_id={this.context.submission_id}
instructor_run={this.props.instructor_run}
instructor_view={!this.props.student_view}
course_id={this.props.course_id}
/>
</div>
</TabPanel>
<TabPanel forceRender={!LeftPane.disableFeedbackFilesPanel(this.props)}>
<TabPanel forceRender={!this.disableFeedbackFilesPanel()}>
<FeedbackFilePanel
assignment_id={this.props.assignment_id}
feedbackFiles={this.props.feedback_files}
submission_id={this.props.submission_id}
course_id={this.props.course_id}
loading={this.props.loading}
/>
</TabPanel>
<TabPanel forceRender={!LeftPane.disableRemarkPanel(this.props)}>
<TabPanel forceRender={!this.disableRemarkPanel()}>
<div id="remark_request_tab">
<RemarkPanel
result_id={this.props.result_id}
assignment_id={this.props.assignment_id}
assignmentRemarkMessage={this.props.assignment_remark_message}
updateOverallComment={this.props.update_overall_comment}
remarkDueDate={this.props.remark_due_date}
Expand All @@ -175,8 +174,6 @@ export class LeftPane extends React.Component {
remarkSubmitted={this.props.remark_submitted}
overallComment={this.props.remark_overall_comment || ""}
studentView={this.props.student_view}
course_id={this.props.course_id}
submission_id={this.props.submission_id}
/>
</div>
</TabPanel>
Expand Down
10 changes: 6 additions & 4 deletions app/javascript/Components/Result/remark_panel.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from "react";
import {render} from "react-dom";
import {TextForm} from "./autosave_text_form";
import {ResultContext} from "./result_context";

export class RemarkPanel extends React.Component {
static contextType = ResultContext;

componentDidMount() {
if (this.props.released_to_students) {
const comment = this.props.overallComment;
Expand All @@ -23,9 +25,9 @@ export class RemarkPanel extends React.Component {
data[name] = "true";
return $.ajax({
url: Routes.update_remark_request_course_assignment_submission_path(
this.props.course_id,
this.props.assignment_id,
this.props.submission_id
this.context.course_id,
this.context.assignment_id,
this.context.submission_id
),
method: "PATCH",
data: data,
Expand Down
Loading

0 comments on commit 4143392

Please sign in to comment.