Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
51 changes: 51 additions & 0 deletions client/common/FinalFormField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Field } from 'react-final-form';
import FormField from './FormField';

function FinalFormField({
name,
validate,
validateFields,
initialValue,
...rest
}) {
return (
<Field
name={name}
validate={validate}
validateFields={validateFields}
initialValue={initialValue}
>
{(field) => (
<FormField
{...rest}
{...field.input}
hasError={field.meta.touched && !!field.meta.error}
error={field.meta.error}
/>
)}
</Field>
);
}

FinalFormField.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
ariaLabel: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
autoComplete: PropTypes.string,
name: PropTypes.string.isRequired,
validate: PropTypes.func,
validateFields: PropTypes.arrayOf(PropTypes.string),
initialValue: PropTypes.string
};

FinalFormField.defaultProps = {
autoComplete: undefined,
validate: undefined,
validateFields: undefined,
initialValue: ''
};

export default FinalFormField;
32 changes: 32 additions & 0 deletions client/common/FormField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import PropTypes from 'prop-types';
import React from 'react';

function FormField({ id, label, ariaLabel, hasError, error, ...rest }) {
return (
<p className="form__field">
<label htmlFor={id} className="form__label">
{label}
</label>
<input className="form__input" aria-label={ariaLabel} id={id} {...rest} />
{hasError && <span className="form-error">{error}</span>}
</p>
);
}

FormField.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
ariaLabel: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
autoComplete: PropTypes.string,
hasError: PropTypes.bool,
error: PropTypes.string
};

FormField.defaultProps = {
autoComplete: null,
hasError: false,
error: null
};

export default FormField;
115 changes: 32 additions & 83 deletions client/modules/User/components/AccountForm.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { Form, Field } from 'react-final-form';
import { Form } from 'react-final-form';
import { useSelector, useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
import Button from '../../../common/Button';
import FinalFormField from '../../../common/FinalFormField';
import { validateSettings } from '../../../utils/reduxFormUtils';
import { updateSettings, initiateVerification } from '../actions';
import apiClient from '../../../utils/apiClient';
Expand Down Expand Up @@ -61,31 +62,17 @@ function AccountForm() {
handleSubmit(event).then(restart);
}}
>
<Field
<FinalFormField
name="email"
id="email"
type="email"
autoComplete="email"
label={t('AccountForm.Email')}
ariaLabel={t('AccountForm.EmailARIA')}
validate={validateEmail}
validateFields={[]}
initialValue={user.email}
>
{(field) => (
<p className="form__field">
<label htmlFor="email" className="form__label">
{t('AccountForm.Email')}
</label>
<input
className="form__input"
aria-label={t('AccountForm.EmailARIA')}
type="email"
id="email"
autoComplete="email"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
/>
{user.verified !== 'verified' && (
<p className="form__context">
<span className="form__status">
Expand All @@ -103,71 +90,33 @@ function AccountForm() {
)}
</p>
)}
<Field
<FinalFormField
name="username"
id="username"
type="text"
autoComplete="username"
label={t('AccountForm.UserName')}
ariaLabel={t('AccountForm.UserNameARIA')}
validate={validateUsername}
validateFields={[]}
initialValue={user.username}
>
{(field) => (
<p className="form__field">
<label htmlFor="username" className="form__label">
{t('AccountForm.UserName')}
</label>
<input
className="form__input"
aria-label={t('AccountForm.UserNameARIA')}
type="text"
id="username"
autoComplete="username"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
<Field name="currentPassword">
{(field) => (
<p className="form__field">
<label htmlFor="current password" className="form__label">
{t('AccountForm.CurrentPassword')}
</label>
<input
className="form__input"
aria-label={t('AccountForm.CurrentPasswordARIA')}
type="password"
id="currentPassword"
autoComplete="current-password"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
<Field name="newPassword">
{(field) => (
<p className="form__field">
<label htmlFor="new password" className="form__label">
{t('AccountForm.NewPassword')}
</label>
<input
className="form__input"
aria-label={t('AccountForm.NewPasswordARIA')}
type="password"
id="newPassword"
autoComplete="new-password"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
/>
<FinalFormField
name="currentPassword"
id="currentPassword"
type="password"
autoComplete="current-password"
label={t('AccountForm.CurrentPassword')}
ariaLabel={t('AccountForm.CurrentPasswordARIA')}
/>
<FinalFormField
name="newPassword"
id="newPassword"
type="password"
autoComplete="new-password"
label={t('AccountForm.NewPassword')}
ariaLabel={t('AccountForm.NewPasswordARIA')}
/>
<Button type="submit" disabled={submitting || invalid}>
{t('AccountForm.SubmitSaveAllSettings')}
</Button>
Expand Down
59 changes: 18 additions & 41 deletions client/modules/User/components/LoginForm.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Form, Field } from 'react-final-form';
import { Form } from 'react-final-form';
import { useDispatch } from 'react-redux';
import Button from '../../../common/Button';
import FinalFormField from '../../../common/FinalFormField';
import { validateLogin } from '../../../utils/reduxFormUtils';
import { validateAndLoginUser } from '../actions';

Expand All @@ -22,46 +23,22 @@ function LoginForm() {
>
{({ handleSubmit, submitError, submitting, modifiedSinceLastSubmit }) => (
<form className="form" onSubmit={handleSubmit}>
<Field name="email">
{(field) => (
<p className="form__field">
<label htmlFor="email" className="form__label">
{t('LoginForm.UsernameOrEmail')}
</label>
<input
className="form__input"
aria-label={t('LoginForm.UsernameOrEmailARIA')}
type="text"
id="email"
autoComplete="username"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
<Field name="password">
{(field) => (
<p className="form__field">
<label htmlFor="password" className="form__label">
{t('LoginForm.Password')}
</label>
<input
className="form__input"
aria-label={t('LoginForm.PasswordARIA')}
type="password"
id="password"
autoComplete="current-password"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
<FinalFormField
name="email"
id="email"
type="text"
autoComplete="username"
label={t('LoginForm.UsernameOrEmail')}
ariaLabel={t('LoginForm.UsernameOrEmailARIA')}
/>
<FinalFormField
name="password"
id="password"
type="password"
autoComplete="current-password"
label={t('LoginForm.Password')}
ariaLabel={t('LoginForm.PasswordARIA')}
/>
{submitError && !modifiedSinceLastSubmit && (
<span className="form-error">{submitError}</span>
)}
Expand Down
59 changes: 18 additions & 41 deletions client/modules/User/components/NewPasswordForm.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Form, Field } from 'react-final-form';
import { Form } from 'react-final-form';
import { useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
import FinalFormField from '../../../common/FinalFormField';
import { validateNewPassword } from '../../../utils/reduxFormUtils';
import { updatePassword } from '../actions';
import Button from '../../../common/Button';
Expand All @@ -24,46 +25,22 @@ function NewPasswordForm(props) {
>
{({ handleSubmit, submitting, invalid, pristine }) => (
<form className="form" onSubmit={handleSubmit}>
<Field name="password">
{(field) => (
<p className="form__field">
<label htmlFor="password" className="form__label">
{t('NewPasswordForm.Title')}
</label>
<input
className="form__input"
aria-label={t('NewPasswordForm.TitleARIA')}
type="password"
id="Password"
autoComplete="new-password"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
<Field name="confirmPassword">
{(field) => (
<p className="form__field">
<label htmlFor="confirm password" className="form__label">
{t('NewPasswordForm.ConfirmPassword')}
</label>
<input
className="form__input"
type="password"
aria-label={t('NewPasswordForm.ConfirmPasswordARIA')}
id="confirm password"
autoComplete="new-password"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
)}
</Field>
<FinalFormField
name="password"
id="Password"
type="password"
autoComplete="new-password"
label={t('NewPasswordForm.Title')}
ariaLabel={t('NewPasswordForm.TitleARIA')}
/>
<FinalFormField
name="confirmPassword"
id="confirmPassword"
type="password"
autoComplete="new-password"
label={t('NewPasswordForm.ConfirmPassword')}
ariaLabel={t('NewPasswordForm.ConfirmPasswordARIA')}
/>
<Button type="submit" disabled={submitting || invalid || pristine}>
{t('NewPasswordForm.SubmitSetNewPassword')}
</Button>
Expand Down
Loading