Refactor LoginForm: use Form connector from Final Form

This commit is contained in:
Vesa Luusua 2018-04-16 19:58:41 +03:00
parent 15fabc7ec8
commit 9cd15f1908
5 changed files with 110 additions and 102 deletions

View file

@ -79,7 +79,7 @@ exports[`AuthenticationPageComponent matches snapshot 1`] = `
]
}
/>
<ReduxForm
<LoginForm
inProgress={false}
onSubmit={[Function]}
/>

View file

@ -4,6 +4,7 @@ import LoginForm from './LoginForm';
export const Empty = {
component: LoginForm,
props: {
formId: 'LoginFormExample',
onSubmit(values) {
console.log('log in with form values:', values);
},

View file

@ -2,123 +2,131 @@ import React from 'react';
import PropTypes from 'prop-types';
import { compose } from 'redux';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { reduxForm, propTypes as formPropTypes } from 'redux-form';
import { Form as FinalForm } from 'react-final-form';
import classNames from 'classnames';
import { Form, PrimaryButton, TextInputField, NamedLink } from '../../components';
import { Form, PrimaryButton, FieldTextInput, NamedLink } from '../../components';
import * as validators from '../../util/validators';
import css from './LoginForm.css';
const LoginFormComponent = props => {
const {
rootClassName,
className,
form,
handleSubmit,
submitting,
inProgress,
intl,
invalid,
} = props;
const LoginFormComponent = props => (
<FinalForm
{...props}
render={fieldRenderProps => {
const {
rootClassName,
className,
formId,
handleSubmit,
submitting,
inProgress,
intl,
invalid,
} = fieldRenderProps;
// email
const emailLabel = intl.formatMessage({
id: 'LoginForm.emailLabel',
});
const emailPlaceholder = intl.formatMessage({
id: 'LoginForm.emailPlaceholder',
});
const emailRequiredMessage = intl.formatMessage({
id: 'LoginForm.emailRequired',
});
const emailRequired = validators.required(emailRequiredMessage);
const emailInvalidMessage = intl.formatMessage({
id: 'LoginForm.emailInvalid',
});
const emailValid = validators.emailFormatValid(emailInvalidMessage);
// email
const emailLabel = intl.formatMessage({
id: 'LoginForm.emailLabel',
});
const emailPlaceholder = intl.formatMessage({
id: 'LoginForm.emailPlaceholder',
});
const emailRequiredMessage = intl.formatMessage({
id: 'LoginForm.emailRequired',
});
const emailRequired = validators.required(emailRequiredMessage);
const emailInvalidMessage = intl.formatMessage({
id: 'LoginForm.emailInvalid',
});
const emailValid = validators.emailFormatValid(emailInvalidMessage);
// password
const passwordLabel = intl.formatMessage({
id: 'LoginForm.passwordLabel',
});
const passwordPlaceholder = intl.formatMessage({
id: 'LoginForm.passwordPlaceholder',
});
const passwordRequiredMessage = intl.formatMessage({
id: 'LoginForm.passwordRequired',
});
const passwordRequired = validators.requiredStringNoTrim(passwordRequiredMessage);
// password
const passwordLabel = intl.formatMessage({
id: 'LoginForm.passwordLabel',
});
const passwordPlaceholder = intl.formatMessage({
id: 'LoginForm.passwordPlaceholder',
});
const passwordRequiredMessage = intl.formatMessage({
id: 'LoginForm.passwordRequired',
});
const passwordRequired = validators.requiredStringNoTrim(passwordRequiredMessage);
const classes = classNames(rootClassName || css.root, className);
const submitInProgress = submitting || inProgress;
const submitDisabled = invalid || submitInProgress;
const classes = classNames(rootClassName || css.root, className);
const submitInProgress = submitting || inProgress;
const submitDisabled = invalid || submitInProgress;
const passwordRecoveryLink = (
<NamedLink name="PasswordRecoveryPage" className={css.recoveryLink}>
<FormattedMessage id="LoginForm.forgotPassword" />
</NamedLink>
);
const passwordRecoveryLink = (
<NamedLink name="PasswordRecoveryPage" className={css.recoveryLink}>
<FormattedMessage id="LoginForm.forgotPassword" />
</NamedLink>
);
return (
<Form className={classes} onSubmit={handleSubmit}>
<div>
<TextInputField
type="email"
name="email"
autoComplete="email"
id={`${form}.email`}
label={emailLabel}
placeholder={emailPlaceholder}
validate={[emailRequired, emailValid]}
/>
<TextInputField
className={css.password}
type="password"
name="password"
autoComplete="current-password"
id={`${form}.password`}
label={passwordLabel}
placeholder={passwordPlaceholder}
validate={passwordRequired}
/>
</div>
<div className={css.bottomWrapper}>
<p className={css.bottomWrapperText}>
<span className={css.recoveryLinkInfo}>
<FormattedMessage id="LoginForm.forgotPasswordInfo" values={{ passwordRecoveryLink }} />
</span>
</p>
<PrimaryButton
className={css.submitButton}
type="submit"
inProgress={submitInProgress}
disabled={submitDisabled}
>
<FormattedMessage id="LoginForm.logIn" />
</PrimaryButton>
</div>
</Form>
);
};
return (
<Form className={classes} onSubmit={handleSubmit}>
<div>
<FieldTextInput
type="email"
id={formId ? `${formId}.email` : 'email'}
name="email"
autoComplete="email"
label={emailLabel}
placeholder={emailPlaceholder}
validate={validators.composeValidators(emailRequired, emailValid)}
/>
<FieldTextInput
className={css.password}
type="password"
id={formId ? `${formId}.password` : 'password'}
name="password"
autoComplete="current-password"
label={passwordLabel}
placeholder={passwordPlaceholder}
validate={passwordRequired}
/>
</div>
<div className={css.bottomWrapper}>
<p className={css.bottomWrapperText}>
<span className={css.recoveryLinkInfo}>
<FormattedMessage
id="LoginForm.forgotPasswordInfo"
values={{ passwordRecoveryLink }}
/>
</span>
</p>
<PrimaryButton
className={css.submitButton}
type="submit"
inProgress={submitInProgress}
disabled={submitDisabled}
>
<FormattedMessage id="LoginForm.logIn" />
</PrimaryButton>
</div>
</Form>
);
}}
/>
);
LoginFormComponent.defaultProps = {
rootClassName: null,
className: null,
form: null,
inProgress: false,
};
const { string, bool } = PropTypes;
LoginFormComponent.propTypes = {
...formPropTypes,
rootClassName: string,
className: string,
form: string,
inProgress: bool,
intl: intlShape.isRequired,
};
const defaultFormName = 'LoginForm';
const LoginForm = compose(reduxForm({ form: defaultFormName }), injectIntl)(LoginFormComponent);
const LoginForm = compose(injectIntl)(LoginFormComponent);
LoginForm.displayName = 'LoginForm';
export default LoginForm;

View file

@ -1,10 +1,13 @@
import React from 'react';
import { renderDeep } from '../../util/test-helpers';
import { fakeIntl } from '../../util/test-data';
import LoginForm from './LoginForm';
const noop = () => null;
describe('LoginForm', () => {
it('matches snapshot', () => {
const tree = renderDeep(<LoginForm />);
const tree = renderDeep(<LoginForm intl={fakeIntl} onSubmit={noop} />);
expect(tree).toMatchSnapshot();
});
});

View file

@ -12,19 +12,17 @@ exports[`LoginForm matches snapshot 1`] = `
className=""
>
<label
htmlFor="LoginForm.email"
htmlFor="email"
>
LoginForm.emailLabel
</label>
<input
autoComplete="email"
className=""
id="LoginForm.email"
id="email"
name="email"
onBlur={[Function]}
onChange={[Function]}
onDragStart={[Function]}
onDrop={[Function]}
onFocus={[Function]}
placeholder="LoginForm.emailPlaceholder"
type="email"
@ -35,19 +33,17 @@ exports[`LoginForm matches snapshot 1`] = `
className=""
>
<label
htmlFor="LoginForm.password"
htmlFor="password"
>
LoginForm.passwordLabel
</label>
<input
autoComplete="current-password"
className=""
id="LoginForm.password"
id="password"
name="password"
onBlur={[Function]}
onChange={[Function]}
onDragStart={[Function]}
onDrop={[Function]}
onFocus={[Function]}
placeholder="LoginForm.passwordPlaceholder"
type="password"