Paragon form component deprecations (#612)

* refactor: removed deprecated paragon components from CoachingToggle and used alternatives

* refactor: removed deprecated paragon components from ConfirmationModal and used alternatives

* refactor: removed deprecations from EditableField and created separate component for SelectField

* refactor: updated DemographicsSection to use new select component

* refactor: removed deprecations from EmailField and used alternatives

* refactor: removed deprecated Input from CoachingConsentForm

* refactor: removed deprecated Input from DemographicsSection

* refactor: removed deprecated Input from SummaryPanel component

* refactor: removed deprecated CheckBox and used Form.CheckBox

* refactor: fixed unit tests

* refactor: changes based on PR reviews

* fix: linting issue
This commit is contained in:
Muhammad Abdullah Waheed
2022-11-08 18:42:09 +05:00
committed by GitHub
parent f78420511e
commit 547d55a31f
12 changed files with 579 additions and 321 deletions

View File

@@ -31,6 +31,7 @@ import PageLoading from './PageLoading';
import JumpNav from './JumpNav';
import DeleteAccount from './delete-account';
import EditableField from './EditableField';
import EditableSelectField from './EditableSelectField';
import ResetPassword from './reset-password';
import NameChange from './name-change';
import ThirdPartyAuth from './third-party-auth';
@@ -609,7 +610,7 @@ class AccountSettingsPage extends React.Component {
<ResetPassword email={this.props.formValues.email} />
{(!getConfig().ENABLE_COPPA_COMPLIANCE)
&& (
<EditableField
<EditableSelectField
name="year_of_birth"
type="select"
label={this.props.intl.formatMessage(messages['account.settings.field.dob'])}
@@ -619,7 +620,7 @@ class AccountSettingsPage extends React.Component {
{...editableFieldProps}
/>
)}
<EditableField
<EditableSelectField
name="country"
type="select"
value={this.props.formValues.country}
@@ -635,7 +636,7 @@ class AccountSettingsPage extends React.Component {
/>
{showState
&& (
<EditableField
<EditableSelectField
name="state"
type="select"
value={this.props.formValues.state}
@@ -657,16 +658,18 @@ class AccountSettingsPage extends React.Component {
{this.props.intl.formatMessage(messages['account.settings.section.profile.information'])}
</h2>
<EditableField
<EditableSelectField
name="level_of_education"
type="select"
value={this.props.formValues.level_of_education}
options={educationLevelOptions}
options={getConfig().ENABLE_COPPA_COMPLIANCE
? educationLevelOptions.filter(option => option.value !== 'el')
: educationLevelOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.education'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.education.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="gender"
type="select"
value={this.props.formValues.gender}
@@ -675,7 +678,7 @@ class AccountSettingsPage extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.gender.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="language_proficiencies"
type="select"
value={this.props.formValues.language_proficiencies}
@@ -738,7 +741,7 @@ class AccountSettingsPage extends React.Component {
</h2>
<BetaLanguageBanner />
<EditableField
<EditableSelectField
name="siteLanguage"
type="select"
options={this.props.siteLanguageOptions}
@@ -747,7 +750,7 @@ class AccountSettingsPage extends React.Component {
helpText={this.props.intl.formatMessage(messages['account.settings.field.site.language.help.text'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="time_zone"
type="select"
value={this.props.formValues.time_zone}

View File

@@ -1,11 +1,9 @@
/* eslint-disable react/jsx-no-useless-fragment */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Button, Input, StatefulButton, ValidationFormGroup,
Button, Form, StatefulButton,
} from '@edx/paragon';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -28,7 +26,6 @@ const EditableField = (props) => {
type,
value,
userSuppliedValue,
options,
saveState,
error,
confirmationMessageDefinition,
@@ -45,10 +42,6 @@ const EditableField = (props) => {
...others
} = props;
const id = `field-${name}`;
let inputOptions = options;
if (getConfig().ENABLE_COPPA_COMPLIANCE && name === 'level_of_education' && options) {
inputOptions = options.filter(option => option.value !== 'el');
}
const handleSubmit = (e) => {
e.preventDefault();
@@ -80,15 +73,6 @@ const EditableField = (props) => {
}
let finalValue = rawValue;
if (options) {
// Use == instead of === to prevent issues when HTML casts numbers as strings
// eslint-disable-next-line eqeqeq
const selectedOption = options.find(option => option.value == rawValue);
if (selectedOption) {
finalValue = selectedOption.label;
}
}
if (userSuppliedValue) {
finalValue += `: ${userSuppliedValue}`;
}
@@ -112,25 +96,24 @@ const EditableField = (props) => {
editing: (
<>
<form onSubmit={handleSubmit}>
<ValidationFormGroup
for={id}
invalid={error != null}
invalidMessage={error}
helpText={helpText}
<Form.Group
controlId={id}
isInvalid={error != null}
>
<label className="h6 d-block" htmlFor={id}>{label}</label>
<Input
<Form.Label size="sm" className="h6 d-block" htmlFor={id}>{label}</Form.Label>
<Form.Control
data-hj-suppress
name={name}
id={id}
type={type}
value={value}
onChange={handleChange}
options={inputOptions}
{...others}
/>
<>{others.children}</>
</ValidationFormGroup>
{!!helpText && <Form.Text>{helpText}</Form.Text>}
{error != null && <Form.Control.Feedback hasIcon={false}>{error}</Form.Control.Feedback>}
{others.children}
</Form.Group>
<p>
<StatefulButton
type="submit"
@@ -188,10 +171,6 @@ EditableField.propTypes = {
type: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
userSuppliedValue: PropTypes.string,
options: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
error: PropTypes.string,
confirmationMessageDefinition: PropTypes.shape({
@@ -213,7 +192,6 @@ EditableField.propTypes = {
EditableField.defaultProps = {
value: undefined,
options: undefined,
saveState: undefined,
label: undefined,
emptyLabel: undefined,

View File

@@ -0,0 +1,232 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Button, Form, StatefulButton,
} from '@edx/paragon';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import SwitchContent from './SwitchContent';
import messages from './AccountSettingsPage.messages';
import {
openForm,
closeForm,
} from './data/actions';
import { editableFieldSelector } from './data/selectors';
import CertificatePreference from './certificate-preference/CertificatePreference';
const EditableSelectField = (props) => {
const {
name,
label,
emptyLabel,
type,
value,
userSuppliedValue,
options,
saveState,
error,
confirmationMessageDefinition,
confirmationValue,
helpText,
onEdit,
onCancel,
onSubmit,
onChange,
isEditing,
isEditable,
isGrayedOut,
intl,
...others
} = props;
const id = `field-${name}`;
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(name, new FormData(e.target).get(name));
};
const handleChange = (e) => {
onChange(name, e.target.value);
};
const handleEdit = () => {
onEdit(name);
};
const handleCancel = () => {
onCancel(name);
};
const renderEmptyLabel = () => {
if (isEditable) {
return <Button variant="link" onClick={handleEdit} className="p-0">{emptyLabel}</Button>;
}
return <span className="text-muted">{emptyLabel}</span>;
};
const renderValue = (rawValue) => {
if (!rawValue) {
return renderEmptyLabel();
}
let finalValue = rawValue;
if (options) {
// Use == instead of === to prevent issues when HTML casts numbers as strings
// eslint-disable-next-line eqeqeq
const selectedOption = options.find(option => option.value == rawValue);
if (selectedOption) {
finalValue = selectedOption.label;
}
}
if (userSuppliedValue) {
finalValue += `: ${userSuppliedValue}`;
}
return finalValue;
};
const renderConfirmationMessage = () => {
if (!confirmationMessageDefinition || !confirmationValue) {
return null;
}
return intl.formatMessage(confirmationMessageDefinition, {
value: confirmationValue,
});
};
const selectOptions = options.map(option => (
<option value={option.value} key={`${option.value}-${option.label}`}>{option.label}</option>
));
return (
<SwitchContent
expression={isEditing ? 'editing' : 'default'}
cases={{
editing: (
<>
<form onSubmit={handleSubmit}>
<Form.Group
controlId={id}
isInvalid={error != null}
>
<Form.Label size="sm" className="h6 d-block" htmlFor={id}>{label}</Form.Label>
<Form.Control
data-hj-suppress
name={name}
id={id}
type={type}
as={type}
value={value}
onChange={handleChange}
{...others}
>
{options.length > 0 && selectOptions}
</Form.Control>
{!!helpText && <Form.Text>{helpText}</Form.Text>}
{error != null && <Form.Control.Feedback>{error}</Form.Control.Feedback>}
{others.children}
</Form.Group>
<p>
<StatefulButton
type="submit"
className="mr-2"
state={saveState}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.action.save']),
}}
onClick={(e) => {
// Swallow clicks if the state is pending.
// We do this instead of disabling the button to prevent
// it from losing focus (disabled elements cannot have focus).
// Disabling it would causes upstream issues in focus management.
// Swallowing the onSubmit event on the form would be better, but
// we would have to add that logic for every field given our
// current structure of the application.
if (saveState === 'pending') { e.preventDefault(); }
}}
disabledStates={[]}
/>
<Button
variant="outline-primary"
onClick={handleCancel}
>
{intl.formatMessage(messages['account.settings.editable.field.action.cancel'])}
</Button>
</p>
</form>
{['name', 'verified_name'].includes(name) && <CertificatePreference fieldName={name} />}
</>
),
default: (
<div className="form-group">
<div className="d-flex align-items-start">
<h6 aria-level="3">{label}</h6>
{isEditable ? (
<Button variant="link" onClick={handleEdit} className="ml-3">
<FontAwesomeIcon className="mr-1" icon={faPencilAlt} />{intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
</Button>
) : null}
</div>
<p data-hj-suppress className={isGrayedOut ? 'grayed-out' : null}>{renderValue(value)}</p>
<p className="small text-muted mt-n2">{renderConfirmationMessage() || helpText}</p>
</div>
),
}}
/>
);
};
EditableSelectField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.node]),
emptyLabel: PropTypes.node,
type: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
userSuppliedValue: PropTypes.string,
options: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
error: PropTypes.string,
confirmationMessageDefinition: PropTypes.shape({
id: PropTypes.string.isRequired,
defaultMessage: PropTypes.string.isRequired,
description: PropTypes.string,
}),
confirmationValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
helpText: PropTypes.node,
onEdit: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
isEditing: PropTypes.bool,
isEditable: PropTypes.bool,
isGrayedOut: PropTypes.bool,
intl: intlShape.isRequired,
};
EditableSelectField.defaultProps = {
value: undefined,
options: [],
saveState: undefined,
label: undefined,
emptyLabel: undefined,
error: undefined,
confirmationMessageDefinition: undefined,
confirmationValue: undefined,
helpText: undefined,
isEditing: false,
isEditable: true,
isGrayedOut: false,
userSuppliedValue: undefined,
};
export default connect(editableFieldSelector, {
onEdit: openForm,
onCancel: closeForm,
})(injectIntl(EditableSelectField));

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import {
Button, StatefulButton, Input, ValidationFormGroup,
Button, StatefulButton, Form,
} from '@edx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
@@ -106,14 +106,12 @@ const EmailField = (props) => {
cases={{
editing: (
<form onSubmit={handleSubmit}>
<ValidationFormGroup
for={id}
invalid={error != null}
invalidMessage={error}
helpText={helpText}
<Form.Group
controlId={id}
isInvalid={error != null}
>
<label className="h6 d-block" htmlFor={id}>{label}</label>
<Input
<Form.Label className="h6 d-block" htmlFor={id}>{label}</Form.Label>
<Form.Control
data-hj-suppress
name={name}
id={id}
@@ -121,7 +119,9 @@ const EmailField = (props) => {
value={value}
onChange={handleChange}
/>
</ValidationFormGroup>
{!!helpText && <Form.Text>{helpText}</Form.Text>}
{error != null && <Form.Control.Feedback hasIcon={false}>{error}</Form.Control.Feedback>}
</Form.Group>
<p>
<StatefulButton
type="submit"

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { Input, Button, Hyperlink } from '@edx/paragon';
import { Form, Button, Hyperlink } from '@edx/paragon';
import PropTypes from 'prop-types';
@@ -38,7 +38,7 @@ const CoachingForm = (props) => (
<label className="h6" htmlFor="fullName">
{props.intl.formatMessage(messages['account.settings.coaching.consent.label.name'])}
</label>
<Input
<Form.Control
type="text"
name="full-name"
id="fullName"
@@ -51,7 +51,7 @@ const CoachingForm = (props) => (
<label className="h6" htmlFor="phoneNumber">
{props.intl.formatMessage(messages['account.settings.coaching.consent.label.phone-number'])}
</label>
<Input
<Form.Control
type="text"
name="phone_number"
id="phoneNumber"

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { ValidationFormGroup, Input } from '@edx/paragon';
import { Form } from '@edx/paragon';
import messages from './CoachingToggle.messages';
import { editableFieldSelector } from '../data/selectors';
import { saveSettings, updateDraft, saveMultipleSettings } from '../data/actions';
@@ -37,20 +37,18 @@ const CoachingToggle = (props) => (
return props.saveSettings('phone_number', props.phone_number);
}}
/>
<ValidationFormGroup
for="coachingConsent"
helpText={props.intl.formatMessage(messages['account.settings.field.coaching_consent.tooltip'])}
invalid={!!props.error}
invalidMessage={props.intl.formatMessage(messages['account.settings.field.coaching_consent.error'])}
<Form.Group
isInvalid={!!props.error}
className="custom-control custom-switch"
>
<Input
<Form.Switch
name={props.name}
className="custom-control-input"
disabled={props.saveState === 'pending'}
type="checkbox"
id="coachingConsent"
checked={props.coaching.coaching_consent}
helperText={props.intl.formatMessage(messages['account.settings.field.coaching_consent.tooltip'])}
value={props.coaching.coaching_consent}
onChange={async (e) => {
const { name } = e.target;
@@ -64,9 +62,15 @@ const CoachingToggle = (props) => (
};
props.saveSettings(name, value);
}}
/>
<label className="custom-control-label" htmlFor="coachingConsent">{props.intl.formatMessage(messages['account.settings.field.coaching_consent'])}</label>
</ValidationFormGroup>
>
{props.intl.formatMessage(messages['account.settings.field.coaching_consent'])}
</Form.Switch>
{!!props.error && (
<Form.Control.Feedback>
{props.intl.formatMessage(messages['account.settings.field.coaching_consent.error'])}
</Form.Control.Feedback>
)}
</Form.Group>
</>
);

View File

@@ -52,14 +52,19 @@ exports[`CoachingConsent disables name field on enterprise user 1`] = `
>
Please confirm your name
</label>
<input
className="form-control"
defaultValue="edx edx"
disabled={true}
id="fullName"
name="full-name"
type="text"
/>
<div
className="pgn__form-control-decorator-group"
>
<input
className="has-value form-control"
defaultValue="edx edx"
disabled={true}
id="fullName"
name="full-name"
onBlur={[Function]}
type="text"
/>
</div>
</div>
<div
className="py-3"
@@ -75,13 +80,18 @@ exports[`CoachingConsent disables name field on enterprise user 1`] = `
>
Enter your mobile number
</label>
<input
className="form-control"
defaultValue="1234567890"
id="phoneNumber"
name="phone_number"
type="text"
/>
<div
className="pgn__form-control-decorator-group"
>
<input
className="has-value form-control"
defaultValue="1234567890"
id="phoneNumber"
name="phone_number"
onBlur={[Function]}
type="text"
/>
</div>
</div>
<div
className=" py-3"
@@ -201,14 +211,19 @@ exports[`CoachingConsent should render 1`] = `
>
Please confirm your name
</label>
<input
className="form-control"
defaultValue="edx edx"
disabled={false}
id="fullName"
name="full-name"
type="text"
/>
<div
className="pgn__form-control-decorator-group"
>
<input
className="has-value form-control"
defaultValue="edx edx"
disabled={false}
id="fullName"
name="full-name"
onBlur={[Function]}
type="text"
/>
</div>
</div>
<div
className="py-3"
@@ -224,13 +239,18 @@ exports[`CoachingConsent should render 1`] = `
>
Enter your mobile number
</label>
<input
className="form-control"
defaultValue="1234567890"
id="phoneNumber"
name="phone_number"
type="text"
/>
<div
className="pgn__form-control-decorator-group"
>
<input
className="has-value form-control"
defaultValue="1234567890"
id="phoneNumber"
name="phone_number"
onBlur={[Function]}
type="text"
/>
</div>
</div>
<div
className=" py-3"

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
Button, Input, Modal, ValidationFormGroup,
Button, Modal, Form,
} from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { faExclamationCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
@@ -100,22 +100,24 @@ export class ConfirmationModal extends Component {
<PrintingInstructions />
</p>
</Alert>
<ValidationFormGroup
for={passwordFieldId}
invalid={errorType !== null}
invalidMessage={intl.formatMessage(invalidMessage)}
<Form.Group
controlId={passwordFieldId}
isInvalid={errorType !== null}
>
<label className="d-block" htmlFor={passwordFieldId}>
<Form.Label className="d-block" htmlFor={passwordFieldId}>
{intl.formatMessage(messages['account.settings.delete.account.modal.enter.password'])}
</label>
<Input
</Form.Label>
<Form.Control
name="password"
id={passwordFieldId}
type="password"
value={password}
onChange={onChange}
/>
</ValidationFormGroup>
{errorType !== null && (
<Form.Control.Feedback>{intl.formatMessage(invalidMessage)}</Form.Control.Feedback>
)}
</Form.Group>
</div>
)}
buttons={[

View File

@@ -91,29 +91,27 @@ Array [
</div>
</div>
<div
className="form-group"
className="pgn__form-group"
>
<label
className="d-block"
className="pgn__form-label d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby=""
className="form-control"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
<div
className="pgn__form-control-decorator-group"
>
Unable to delete account
</strong>
<input
className="has-value form-control"
id="passwordFieldId"
name="password"
onBlur={[Function]}
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
</div>
</div>
</div>
</div>
@@ -170,7 +168,7 @@ Array [
role="presentation"
>
<div
aria-labelledby="id3"
aria-labelledby="id5"
aria-modal={true}
className="modal-dialog"
role="dialog"
@@ -188,16 +186,14 @@ Array [
"width": "1px",
}
}
tabIndex={0}
tabIndex={-1}
/>
<div
data-focus-lock-disabled={false}
data-focus-lock-disabled="disabled"
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onScrollCapture={[Function]}
onTouchMoveCapture={[Function]}
onTouchStart={[Function]}
onWheelCapture={[Function]}
>
<div
@@ -208,7 +204,7 @@ Array [
>
<h2
className="modal-title"
id="id3"
id="id5"
>
Are you sure?
</h2>
@@ -285,29 +281,210 @@ Array [
</div>
</div>
<div
className="form-group"
className="pgn__form-group"
>
<label
className="d-block"
className="pgn__form-label d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby="passwordFieldId-invalid-feedback"
className="form-control is-invalid"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
<div
className="pgn__form-control-decorator-group"
>
<input
className="has-value form-control is-invalid"
id="passwordFieldId"
name="password"
onBlur={[Function]}
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
</div>
<div
className="pgn__form-control-description pgn__form-text pgn__form-text-invalid"
>
<span
className="pgn__icon"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"
fill="currentColor"
/>
</svg>
</span>
A password is required
</strong>
</div>
</div>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
<button
className="btn btn-danger"
disabled={false}
onClick={[MockFunction]}
type="button"
>
Yes, Delete
</button>
</div>
</div>
</div>
<div
data-focus-guard={true}
style={
Object {
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={-1}
/>
</div>
</div>,
]
`;
exports[`ConfirmationModal should match open confirmation modal snapshot 1`] = `
Array [
<div
className="modal-backdrop show"
role="presentation"
/>,
<div
className="modal show d-block"
role="presentation"
>
<div
aria-labelledby="id3"
aria-modal={true}
className="modal-dialog"
role="dialog"
>
<div
data-focus-guard={true}
style={
Object {
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={0}
/>
<div
data-focus-lock-disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onScrollCapture={[Function]}
onTouchMoveCapture={[Function]}
onTouchStart={[Function]}
onWheelCapture={[Function]}
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id3"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
>
<div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. localhost will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on localhost.
</p>
<p>
You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.
</p>
</div>
</div>
<div
className="pgn__form-group"
>
<label
className="pgn__form-label d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<div
className="pgn__form-control-decorator-group"
>
<input
className="has-value form-control"
id="passwordFieldId"
name="password"
onBlur={[Function]}
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
</div>
</div>
</div>
</div>
@@ -352,162 +529,3 @@ Array [
</div>,
]
`;
exports[`ConfirmationModal should match open confirmation modal snapshot 1`] = `
Array [
<div
className="modal-backdrop show"
role="presentation"
/>,
<div
className="modal show d-block"
role="presentation"
>
<div
aria-labelledby="id2"
aria-modal={true}
className="modal-dialog"
role="dialog"
>
<div
data-focus-guard={true}
style={
Object {
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={-1}
/>
<div
data-focus-lock-disabled="disabled"
onBlur={[Function]}
onFocus={[Function]}
onScrollCapture={[Function]}
onTouchMoveCapture={[Function]}
onWheelCapture={[Function]}
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id2"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
>
<div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. localhost will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on localhost.
</p>
<p>
You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby=""
className="form-control"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
>
Unable to delete account
</strong>
</div>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
<button
className="btn btn-danger"
disabled={false}
onClick={[MockFunction]}
type="button"
>
Yes, Delete
</button>
</div>
</div>
</div>
<div
data-focus-guard={true}
style={
Object {
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={-1}
/>
</div>
</div>,
]
`;

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { CheckBox } from '@edx/paragon';
import { Form } from '@edx/paragon';
import { DECLINED } from '../data/constants';
const Checkboxes = (props) => {
@@ -40,16 +40,17 @@ const Checkboxes = (props) => {
const isChecked = selected.includes(option.value);
return (
<div key={option.value} className="checkboxOption">
<CheckBox
<Form.Checkbox
type="checkbox"
id={option.value}
name={option.value}
value={option.value}
checked={isChecked}
autoFocus={isFirst}
label={option.label}
onChange={(value) => handleToggle(value, option.value)}
/>
onChange={(event) => handleToggle(event.target.checked, option.value)}
>
{option.label}
</Form.Checkbox>
</div>
);
});

View File

@@ -5,7 +5,7 @@ import {
intlShape,
} from '@edx/frontend-platform/i18n';
import { Hyperlink, Input } from '@edx/paragon';
import { Hyperlink, Form } from '@edx/paragon';
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
@@ -13,7 +13,7 @@ import get from 'lodash.get';
import isEmpty from 'lodash.isempty';
import memoize from 'memoize-one';
import { demographicsSectionSelector } from '../data/selectors';
import EditableField from '../EditableField';
import EditableSelectField from '../EditableSelectField';
import Checkboxes from './Checkboxes';
import Alert from '../Alert';
import { saveMultipleSettings, updateDraft } from '../data/actions';
@@ -188,7 +188,7 @@ class DemographicsSection extends React.Component {
*/}
{this.hasRetrievedDemographicsOptions() && (
<div id="demographics-fields">
<EditableField
<EditableSelectField
name="demographics_gender"
type="select"
value={this.props.formValues.demographics_gender}
@@ -199,7 +199,7 @@ class DemographicsSection extends React.Component {
{...editableFieldProps}
>
{showSelfDescribe && (
<Input
<Form.Control
name="demographics_gender_description"
id="field-demographics_gender_description"
type="text"
@@ -210,8 +210,8 @@ class DemographicsSection extends React.Component {
className="mt-1"
/>
)}
</EditableField>
<EditableField
</EditableSelectField>
<EditableSelectField
name="demographics_user_ethnicity"
type="select"
hidden
@@ -226,8 +226,8 @@ class DemographicsSection extends React.Component {
values={this.props.formValues.demographics_user_ethnicity}
{...editableFieldProps}
/>
</EditableField>
<EditableField
</EditableSelectField>
<EditableSelectField
name="demographics_income"
type="select"
value={this.props.formValues.demographics_income}
@@ -236,7 +236,7 @@ class DemographicsSection extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.income.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="demographics_military_history"
type="select"
value={this.props.formValues.demographics_military_history}
@@ -245,7 +245,7 @@ class DemographicsSection extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.military_history.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="demographics_learner_education_level"
type="select"
value={this.props.formValues.demographics_learner_education_level}
@@ -254,7 +254,7 @@ class DemographicsSection extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.learner_education_level.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="demographics_parent_education_level"
type="select"
value={this.props.formValues.demographics_parent_education_level}
@@ -263,7 +263,7 @@ class DemographicsSection extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.parent_education_level.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="demographics_work_status"
type="select"
value={this.props.formValues.demographics_work_status}
@@ -276,7 +276,7 @@ class DemographicsSection extends React.Component {
{...editableFieldProps}
>
{showWorkStatusDescribe && (
<Input
<Form.Control
name="demographics_work_status_description"
id="field-demographics_work_status_description"
type="text"
@@ -287,8 +287,8 @@ class DemographicsSection extends React.Component {
className="mt-1"
/>
)}
</EditableField>
<EditableField
</EditableSelectField>
<EditableSelectField
name="demographics_current_work_sector"
type="select"
value={this.props.formValues.demographics_current_work_sector}
@@ -297,7 +297,7 @@ class DemographicsSection extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.current_work_sector.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="demographics_future_work_sector"
type="select"
value={this.props.formValues.demographics_future_work_sector}

View File

@@ -1,7 +1,7 @@
import React, { useState, useContext, useEffect } from 'react';
import { getConfig, history } from '@edx/frontend-platform';
import {
Alert, Hyperlink, Input, Button, Spinner,
Alert, Hyperlink, Form, Button, Spinner,
} from '@edx/paragon';
import { Link } from 'react-router-dom';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
@@ -208,7 +208,7 @@ const SummaryPanel = (props) => {
</label>
{renderManagedProfileMessage()}
<div className="d-flex">
<Input
<Form.Control
id="name-to-be-used"
type="text"
disabled