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:
Michael Roytman
2020-07-23 14:07:14 -04:00
committed by GitHub
parent 8aef7877d9
commit 39b0320ae8
6 changed files with 450 additions and 328 deletions

View File

@@ -4,3 +4,5 @@
@import "~@edx/frontend-component-header/dist/index";
@import "~@edx/frontend-component-footer/dist/footer";
@import "proctored-exam-settings/proctoredExamSettings.scss";

View File

@@ -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 &quot;Yes&quot;, learners can choose to take proctored exams without proctoring.
If this value is &quot;No&quot;, 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 &quot;proctortrack&quot; 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 &quot;Yes&quot;, 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>
);
}

View File

@@ -0,0 +1,5 @@
// legend styling should match label styling
legend {
font-size: 1.0rem;
font-weight: 700;
}