Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grading: Scroll down to complaint form after clicking on a complain button #9762

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
}
</p>
}

@if (isCorrectUserToFileAction && !complaint) {
<div class="mt-4">
@if (isExamMode || (course?.maxComplaints && course!.maxComplaints! > 0)) {
<button
id="complain"
class="btn btn-primary"
[class.not-allowed]="(!isExamMode && remainingNumberOfComplaints === 0) || !timeOfComplaintValid"
(click)="formComplaintType = ComplaintType.COMPLAINT"
(click)="openComplaintForm(ComplaintType.COMPLAINT)"
[disabled]="(!isExamMode && remainingNumberOfComplaints === 0) || !timeOfComplaintValid"
title="{{
(!isExamMode && remainingNumberOfComplaints === 0) || !timeOfComplaintValid
Expand All @@ -35,9 +36,10 @@
}
@if (!isExamMode && course?.requestMoreFeedbackEnabled) {
<button
id="more-feedback"
class="btn btn-primary ms-1"
[class.not-allowed]="!timeOfFeedbackRequestValid"
(click)="formComplaintType = ComplaintType.MORE_FEEDBACK"
(click)="openComplaintForm(ComplaintType.MORE_FEEDBACK)"
[disabled]="!timeOfFeedbackRequestValid"
title="{{ !timeOfFeedbackRequestValid ? ('artemisApp.moreFeedback.notAllowedTooltip' | artemisTranslate) : '' }}"
jhiTranslate="artemisApp.moreFeedback.button"
Expand All @@ -57,6 +59,7 @@
(onSubmit)="loadPotentialComplaint()"
/>
</div>
<div #complaintScrollpoint></div>
}
@if (complaint) {
<div class="row">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core';
import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { Exercise, getCourseFromExercise } from 'app/entities/exercise.model';
import { Complaint, ComplaintType } from 'app/entities/complaint.model';
import { ComplaintService } from 'app/complaints/complaint.service';
Expand Down Expand Up @@ -28,6 +28,8 @@ export class ComplaintsStudentViewComponent implements OnInit {
// flag to indicate exam test run. Default set to false.
@Input() testRun = false;

@ViewChild('complaintScrollpoint') complaintScrollpoint: ElementRef;

submission: Submission;
complaint: Complaint;
course?: Course;
Expand All @@ -51,6 +53,7 @@ export class ComplaintsStudentViewComponent implements OnInit {
private serverDateService: ArtemisServerDateService,
private accountService: AccountService,
private courseService: CourseManagementService,
private cdr: ChangeDetectorRef,
) {}

/**
Expand Down Expand Up @@ -149,4 +152,21 @@ export class ComplaintsStudentViewComponent implements OnInit {
}
return false;
}

/**
* Function to set complaint type (which opens the complaint form) and scrolls to the complaint form
*/
openComplaintForm(complainType: ComplaintType): void {
this.formComplaintType = complainType;
// Wait for the view to update
this.cdr.detectChanges();
this.scrollToComplaint();
}

/**
* Function to scroll to the complaint form
*/
private scrollToComplaint(): void {
this.complaintScrollpoint?.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'end' });
}
badkeyy marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { AssessmentType } from 'app/entities/assessment-type.model';
import { TranslateDirective } from 'app/shared/language/translate.directive';
import { CourseManagementService } from 'app/course/manage/course-management.service';
import { MockCourseManagementService } from '../../helpers/mocks/service/mock-course-management.service';
import { ElementRef } from '@angular/core';
import { ComplaintType } from 'app/entities/complaint.model';

describe('ComplaintsStudentViewComponent', () => {
const complaintTimeLimitDays = 7;
Expand Down Expand Up @@ -146,6 +148,39 @@ describe('ComplaintsStudentViewComponent', () => {
expect(userMock).toHaveBeenCalledOnce();
}));

it('should set complaint type COMPLAINT and scroll to complaint form when pressing complaint', fakeAsync(() => {
component.exercise = examExercise;
component.result = result;
component.exam = defaultExam;
component.showSection = true;
component.isCorrectUserToFileAction = true;
const complaintBySubmissionMock = jest.spyOn(complaintService, 'findBySubmissionId').mockReturnValue(of());

fixture.detectChanges();

//Check if button is available
expect(component.complaint).toBeUndefined();
expect(complaintBySubmissionMock).toHaveBeenCalledOnce();

// Mock complaint scrollpoint
const scrollIntoViewMock = jest.fn();
component.complaintScrollpoint = {
nativeElement: {
scrollIntoView: scrollIntoViewMock,
},
} as ElementRef;

const button = fixture.debugElement.nativeElement.querySelector('#complain');
button.click();

fixture.detectChanges();

expect(component.formComplaintType).toBe(ComplaintType.COMPLAINT);
// Wait for setTimeout to execute
tick();
expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth' });
}));

it('should be visible on test run', fakeAsync(() => {
const now = dayjs();
const examWithFutureReview: Exam = { examStudentReviewStart: dayjs(now).add(1, 'day'), examStudentReviewEnd: dayjs(now).add(2, 'day') } as Exam;
Expand Down Expand Up @@ -204,6 +239,71 @@ describe('ComplaintsStudentViewComponent', () => {
expect(component.complaint).toStrictEqual(complaint);
}));

it('should set complaint type COMPLAINT and scroll to complaint form when pressing complaint', fakeAsync(() => {
testInitWithResultStub(of());
const courseWithMaxComplaints: Course = {
...course,
maxComplaints: 3,
};
const exerciseWithMaxComplaints: Exercise = {
...courseExercise,
course: courseWithMaxComplaints,
};
component.course = courseWithMaxComplaints;
component.exercise = exerciseWithMaxComplaints;

component.showSection = true;
component.isCorrectUserToFileAction = true;
component.remainingNumberOfComplaints = 1;

fixture.detectChanges();

// Mock complaint scrollpoint
const scrollIntoViewMock = jest.fn();
component.complaintScrollpoint = {
nativeElement: {
scrollIntoView: scrollIntoViewMock,
},
} as ElementRef;

const button = fixture.debugElement.nativeElement.querySelector('#complain');
button.click();

fixture.detectChanges();

expect(component.formComplaintType).toBe(ComplaintType.COMPLAINT);
tick(); // Wait for update to happen
expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth' });
}));

it('should set complaint type MORE_FEEDBACK and scroll to complaint form when pressing complaint', fakeAsync(() => {
testInitWithResultStub(of());
component.showSection = true;
component.isCorrectUserToFileAction = true;

fixture.detectChanges();

//Check if button is available
expect(component.complaint).toBeUndefined();

// Mock complaint scrollpoint
const scrollIntoViewMock = jest.fn();
component.complaintScrollpoint = {
nativeElement: {
scrollIntoView: scrollIntoViewMock,
},
} as ElementRef;

const button = fixture.debugElement.nativeElement.querySelector('#more-feedback');
button.click();

fixture.detectChanges();

expect(component.formComplaintType).toBe(ComplaintType.MORE_FEEDBACK);
tick(); // Wait for update to happen
expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: 'smooth' });
}));

it('should not be available if before or at assessment due date', fakeAsync(() => {
const exercise: Exercise = { id: 1, teamMode: false, course, assessmentDueDate: dayjs() } as Exercise;
const resultMatchingDate: Result = { id: 1, completionDate: dayjs(exercise.assessmentDueDate) } as Result;
Expand Down