update version of Paragon, change some checkboxes to radio buttons to match wireframes, and update remaining Paragon components to use React-Boostrap passthrough components (#12)
This commit is contained in:
@@ -4,3 +4,5 @@
|
||||
|
||||
@import "~@edx/frontend-component-header/dist/index";
|
||||
@import "~@edx/frontend-component-footer/dist/footer";
|
||||
|
||||
@import "proctored-exam-settings/proctoredExamSettings.scss";
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import EmailValidator from 'email-validator';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
Button, CheckBox, Input, ValidationFormGroup, StatusAlert,
|
||||
Alert, Button, Form, Spinner,
|
||||
} from '@edx/paragon';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
|
||||
@@ -26,15 +26,15 @@ function ExamSettings(props) {
|
||||
const [courseStartDate, setCourseStartDate] = useState('');
|
||||
|
||||
function onEnableProctoredExamsChange(event) {
|
||||
setEnableProctoredExams(event);
|
||||
setEnableProctoredExams(event.target.checked);
|
||||
}
|
||||
|
||||
function onAllowOptingOutChange(event) {
|
||||
setAllowOptingOut(event);
|
||||
function onAllowOptingOutChange(value) {
|
||||
setAllowOptingOut(value);
|
||||
}
|
||||
|
||||
function onCreateZendeskTicketsChange(event) {
|
||||
setCreateZendeskTickets(event);
|
||||
function onCreateZendeskTicketsChange(value) {
|
||||
setCreateZendeskTickets(value);
|
||||
}
|
||||
|
||||
function onProctoringProviderChange(event) {
|
||||
@@ -55,10 +55,10 @@ function ExamSettings(props) {
|
||||
function onButtonClick() {
|
||||
if (proctoringProvider === 'proctortrack' && !EmailValidator.validate(proctortrackEscalationEmail)) {
|
||||
setProctortrackEscalationEmailError('A valid escalation email must be provided if '
|
||||
+ 'Proctortrack is the selected provider.');
|
||||
+ 'Proctortrack is the selected provider.');
|
||||
} else {
|
||||
setProctortrackEscalationEmailError('');
|
||||
// TODO: implement POST
|
||||
// TODO: implement POST
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,138 +81,170 @@ function ExamSettings(props) {
|
||||
}
|
||||
|
||||
function getProctoringProviderOptions(providers) {
|
||||
return providers.reduce(
|
||||
(accumulator, currentValue) => {
|
||||
accumulator.push({
|
||||
value: currentValue,
|
||||
label: currentValue,
|
||||
disabled: isDisabledOption(currentValue),
|
||||
}); return accumulator;
|
||||
}, [],
|
||||
);
|
||||
return providers.map(provider => (
|
||||
<option
|
||||
key={provider}
|
||||
value={provider}
|
||||
disabled={isDisabledOption(provider)}
|
||||
>
|
||||
{provider}
|
||||
</option>
|
||||
));
|
||||
}
|
||||
|
||||
function renderContent() {
|
||||
return (
|
||||
<div className="content-container">
|
||||
<ValidationFormGroup
|
||||
for="enableProctoredExams"
|
||||
helpText="If checked, proctored exams are enabled in your course."
|
||||
>
|
||||
<CheckBox
|
||||
<Form>
|
||||
{proctortrackEscalationEmailError
|
||||
&& (
|
||||
// tabIndex="-1" to make non-focusable element focusable
|
||||
<Alert id="proctortrackEscalationEmailError" variant="danger" tabIndex="-1">
|
||||
{proctortrackEscalationEmailError}
|
||||
</Alert>
|
||||
)}
|
||||
{/* ENABLE PROCTORED EXAMS */}
|
||||
<Form.Group controlId="formEnableProctoredExam">
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
id="enableProctoredExams"
|
||||
name="enable_proctored_exams"
|
||||
label="Enable Proctored Exams"
|
||||
checked={enableProctoredExams}
|
||||
aria-describedby="enableProctoredExamsHelpText"
|
||||
onChange={onEnableProctoredExamsChange}
|
||||
checked={enableProctoredExams}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="allowingOptingOut"
|
||||
helpText="If checked, learners can choose to take proctored exams without proctoring. If not checked, all learners must take the exam with proctoring."
|
||||
>
|
||||
<CheckBox
|
||||
id="allowingOptingOut"
|
||||
name="allow_opting_out"
|
||||
label="Allow Opting Out of Proctored Exams"
|
||||
checked={allowOptingOut}
|
||||
onChange={onAllowOptingOutChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="proctoringProvider"
|
||||
helpText={cannotEditProctoringProvider() ? ('Proctoring provider cannot be modified after course start date.') : ('Select the proctoring provider you want to use for this course run.')}
|
||||
>
|
||||
<label htmlFor="proctoringProvider">Proctoring Provider</label>
|
||||
<Input
|
||||
type="select"
|
||||
id="proctoringProvider"
|
||||
name="proctoring_provider"
|
||||
options={getProctoringProviderOptions(availableProctoringProviders)}
|
||||
<Form.Text id="enableProctoredExamsHelpText">
|
||||
If checked, proctored exams are enabled in your course.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
{/* ALLOW OPTING OUT OF PROCTORED EXAMS */}
|
||||
<fieldset aria-describedby="allowOptingOutHelpText">
|
||||
<Form.Group controlId="formAllowingOptingOut">
|
||||
<Form.Label as="legend">Allow Opting Out of Proctored Exams</Form.Label>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="allowOptingOutYes"
|
||||
name="allowOptingOut"
|
||||
label="Yes"
|
||||
inline
|
||||
checked={allowOptingOut}
|
||||
onChange={() => onAllowOptingOutChange(true)}
|
||||
/>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="allowOptingOutNo"
|
||||
name="allowOptingOut"
|
||||
label="No"
|
||||
inline
|
||||
checked={!allowOptingOut}
|
||||
onChange={() => onAllowOptingOutChange(false)}
|
||||
/>
|
||||
<Form.Text id="allowOptingOutHelpText">
|
||||
If this value is "Yes", learners can choose to take proctored exams without proctoring.
|
||||
If this value is "No", all learners must take the exam with proctoring.
|
||||
This setting only applies if proctored exams are enabled for the course.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
</fieldset>
|
||||
|
||||
{/* PROCTORING PROVIDER */}
|
||||
<Form.Group controlId="formProctoringProvider">
|
||||
<Form.Label>Proctoring Provider</Form.Label>
|
||||
<Form.Control
|
||||
as="select"
|
||||
value={proctoringProvider}
|
||||
onChange={onProctoringProviderChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="proctortrackEscalationEmail"
|
||||
helpText="Required if 'proctortrack' is selected as your proctoring provider. Enter an email address to be contacted by the support team whenever there are escalations (e.g. appeals, delayed reviews, etc.)."
|
||||
>
|
||||
<label htmlFor="proctortrackEscalationEmail">Proctortrack Escalation Email</label>
|
||||
<Input
|
||||
type="text"
|
||||
id="proctortrackEscalationEmail"
|
||||
name="proctortrack_escalation_email"
|
||||
value={proctortrackEscalationEmail}
|
||||
>
|
||||
{getProctoringProviderOptions(availableProctoringProviders)}
|
||||
</Form.Control>
|
||||
<Form.Text>{cannotEditProctoringProvider() ? ('Proctoring provider cannot be modified after course start date.') : ('Select the proctoring provider you want to use for this course run.')}</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
{/* PROCTORTRACK ESCALATION EMAIL */}
|
||||
<Form.Group controlId="formProctortrackEscalationEmail">
|
||||
<Form.Label>Proctortrack Escalation Email</Form.Label>
|
||||
<Form.Control
|
||||
type="email"
|
||||
onChange={onProctortrackEscalationEmailChange}
|
||||
value={proctortrackEscalationEmail}
|
||||
isInvalid={!!proctortrackEscalationEmailError}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="createZendeskTickets"
|
||||
helpText="If checked, a Zendesk ticket will be created for suspicious attempts."
|
||||
>
|
||||
<CheckBox
|
||||
id="createZendeskTickets"
|
||||
name="create_zendesk_tickets"
|
||||
label="Create Zendesk Tickets For Suspicious Exam Attempts"
|
||||
checked={createZendeskTickets}
|
||||
onChange={onCreateZendeskTicketsChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
{ proctortrackEscalationEmailError
|
||||
&& (
|
||||
<StatusAlert
|
||||
alertType="danger"
|
||||
className="proctortrackEscalationEmailError"
|
||||
dialog={proctortrackEscalationEmailError}
|
||||
open
|
||||
dismissible={false}
|
||||
/>
|
||||
)}
|
||||
<Form.Control.Feedback type="invalid">{proctortrackEscalationEmailError}</Form.Control.Feedback>
|
||||
<Form.Text>
|
||||
Required if "proctortrack" is selected as your proctoring provider.
|
||||
Enter an email address to be contacted by the support team whenever there are escalations
|
||||
(e.g. appeals, delayed reviews, etc.).
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
{/* CREATE ZENDESK TICKETS */}
|
||||
<fieldset aria-describedby="allowOptingOutHelpText">
|
||||
<Form.Group controlId="formCreateZendeskTickets">
|
||||
<Form.Label as="legend">Create Zendesk Tickets for Suspicious Proctored Exam Attempts</Form.Label>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="createZendeskTicketsYes"
|
||||
label="Yes"
|
||||
inline
|
||||
name="createZendeskTickets"
|
||||
checked={createZendeskTickets}
|
||||
onChange={() => onCreateZendeskTicketsChange(true)}
|
||||
/>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="createZendeskTicketsNo"
|
||||
label="No"
|
||||
inline
|
||||
name="createZendeskTickets"
|
||||
checked={!createZendeskTickets}
|
||||
onChange={() => onCreateZendeskTicketsChange(false)}
|
||||
/>
|
||||
<Form.Text id="createZendeskTicketsText">
|
||||
If this value is "Yes", a Zendesk ticket will be created for suspicious proctored exam attempts.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
</fieldset>
|
||||
<Button
|
||||
className="btn-primary mb-3"
|
||||
onClick={onButtonClick}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
function renderLoading() {
|
||||
return (
|
||||
<div className="Loading-container">
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center flex-column"
|
||||
style={{
|
||||
height: '50vh',
|
||||
}}
|
||||
>
|
||||
<div className="spinner-border text-primary" role="status" />
|
||||
</div>
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center flex-column"
|
||||
style={{
|
||||
height: '50vh',
|
||||
}}
|
||||
>
|
||||
<Spinner className animation="border" role="status" variant="primary">
|
||||
<span className="sr-only">Loading...</span>
|
||||
</Spinner>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderConnectionError() {
|
||||
return (
|
||||
<div className="justify-content-center align-items-center flex-column">
|
||||
<div className="alert alert-danger">
|
||||
We encountered a technical error when loading this page.
|
||||
This might be a temporary issue, so please try again in a few minutes.
|
||||
If the problem persists, please go to <a href="https://support.edx.org/hc/en-us">edX Support Page</a> for help.
|
||||
</div>
|
||||
</div>
|
||||
<Alert variant="danger">
|
||||
We encountered a technical error when loading this page.
|
||||
This might be a temporary issue, so please try again in a few minutes.
|
||||
If the problem persists, please go to <a href="https://support.edx.org/hc/en-us">edX Support Page</a> for help.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
function renderPermissionError() {
|
||||
return (
|
||||
<div className="justify-content-center align-items-center flex-column">
|
||||
<div className="alert alert-danger">
|
||||
You are not authorized to view this page. If you feel you should have access,
|
||||
please reach out to your course team admin to be given access.
|
||||
</div>
|
||||
</div>
|
||||
<Alert variant="danger">
|
||||
You are not authorized to view this page. If you feel you should have access,
|
||||
please reach out to your course team admin to be given access.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
5
src/proctored-exam-settings/proctoredExamSettings.scss
Normal file
5
src/proctored-exam-settings/proctoredExamSettings.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
// legend styling should match label styling
|
||||
legend {
|
||||
font-size: 1.0rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
Reference in New Issue
Block a user