cspace-ui
Version:
CollectionSpace user interface for browsers
368 lines (312 loc) • 8.33 kB
JSX
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { components as inputComponents } from 'cspace-input';
import LoginButton from './LoginButton';
import Notification from '../notification/Notification';
import styles from '../../../styles/cspace-ui/LoginForm.css';
const { LineInput, PasswordInput } = inputComponents;
const messages = defineMessages({
title: {
id: 'loginForm.title',
description: 'Title displayed above the login form.',
defaultMessage: 'Sign In',
},
prompt: {
id: 'loginForm.prompt',
description: 'The prompt displayed on the login form when the user is not logged in.',
defaultMessage: 'Please sign in to continue.',
},
expiredPrompt: {
id: 'loginForm.expiredPrompt',
description: 'The prompt displayed on the login form when the login session has expired.',
defaultMessage: 'Your session has expired. Please sign in again to continue.',
},
pending: {
id: 'loginForm.pending',
description: 'Message displayed while login is in progress.',
defaultMessage: 'Signing in...',
},
success: {
id: 'loginForm.success',
description: 'Message displayed when login completes successfully.',
defaultMessage: 'Sign in complete.',
},
error: {
id: 'loginForm.error',
description: 'Generic login error message. Displayed when a more specific error message is not available.',
defaultMessage: 'Sign in failed.',
},
ERR_INVALID_CREDENTIALS: {
id: 'loginForm.ERR_INVALID_CREDENTIALS',
description: 'Error message displayed when incorrect credentials were entered during login.',
defaultMessage: 'Sign in failed. Incorrect username/password.',
},
ERR_NETWORK: {
id: 'loginForm.ERR_NETWORK',
description: 'Error message displayed when there is a network error during login.',
defaultMessage: 'Sign in failed. Unable to reach the CollectionSpace server.',
},
ERR_WRONG_TENANT: {
id: 'loginForm.ERR_WRONG_TENANT',
description: 'Error message displayed when the logged in user belongs to the wrong tenant.',
defaultMessage: 'Sign in failed. The user is not registered to this CollectionSpace tenant.',
},
username: {
id: 'loginForm.username',
description: 'Label for the login username field.',
defaultMessage: 'Email',
},
password: {
id: 'loginForm.password',
description: 'Label for the login password field.',
defaultMessage: 'Password',
},
forgotPassword: {
id: 'loginForm.forgotPassword',
description: 'Text of the forgot password link.',
defaultMessage: 'Forgot password',
},
});
const contextTypes = {
config: PropTypes.object,
};
const propTypes = {
formId: PropTypes.string,
intl: PropTypes.object.isRequired,
isExpired: PropTypes.bool,
isPending: PropTypes.bool,
isSuccess: PropTypes.bool,
username: PropTypes.string,
error: PropTypes.object,
showForgotLink: PropTypes.bool,
showHeader: PropTypes.bool,
login: PropTypes.func,
onSuccess: PropTypes.func,
};
const defaultProps = {
showForgotLink: true,
showHeader: true,
};
class LoginForm extends Component {
constructor(props) {
super(props);
this.handlePasswordInputApi = this.handlePasswordInputApi.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleUsernameChange = this.handleUsernameChange.bind(this);
this.handleUsernameInputApi = this.handleUsernameInputApi.bind(this);
this.state = {
username: props.username,
};
}
componentDidMount() {
const {
username,
} = this.props;
// If there is a username, focus the password input. Otherwise, focus the username input.
if (username && this.passwordInputApi) {
this.passwordInputApi.focus();
} else if (this.usernameInputApi) {
this.usernameInputApi.focus();
}
}
componentWillReceiveProps(nextProps) {
this.setState({
username: nextProps.username,
});
}
componentDidUpdate(prevProps) {
const {
error,
isSuccess,
username,
onSuccess,
} = this.props;
const {
error: prevError,
username: prevUsername,
} = prevProps;
if (this.passwordInputApi) {
// If the username has been set, focus the password.
if (username && !prevUsername) {
this.passwordInputApi.focus();
}
// If login fails, focus the password.
if (error && !prevError) {
this.passwordInputApi.focus();
}
}
if (onSuccess && isSuccess && !prevProps.isSuccess) {
onSuccess();
}
}
handlePasswordInputApi(api) {
this.passwordInputApi = api;
}
handleSubmit(event) {
event.preventDefault();
const {
login,
} = this.props;
const {
config,
} = this.context;
if (login) {
const form = event.target;
const username = form.username.value;
const password = form.password.value;
login(config, username, password);
}
}
handleUsernameInputApi(api) {
this.usernameInputApi = api;
}
handleUsernameChange(value) {
this.setState({
username: value,
});
}
renderHeader() {
const {
showHeader,
} = this.props;
if (!showHeader) {
return null;
}
return (
<h2><FormattedMessage {...messages.title} /></h2>
);
}
renderPrompt() {
const {
isExpired,
isPending,
isSuccess,
} = this.props;
let messageKey;
if (isPending) {
messageKey = 'pending';
} else if (isSuccess) {
messageKey = 'success';
} else if (isExpired) {
messageKey = 'expiredPrompt';
} else {
messageKey = 'prompt';
}
return (
<p><FormattedMessage {...messages[messageKey]} /></p>
);
}
renderButtonBar() {
const {
showForgotLink,
} = this.props;
const {
username,
} = this.state;
let forgotLink;
if (showForgotLink) {
forgotLink = (
<Link
to={{
pathname: '/resetpw',
state: {
username,
},
}}
>
<FormattedMessage {...messages.forgotPassword} />
</Link>
);
} else {
forgotLink = <div />;
}
return (
<div>
{forgotLink}
<LoginButton type="submit" />
</div>
);
}
renderForm() {
const {
formId,
intl,
isPending,
isSuccess,
} = this.props;
if (isPending || isSuccess) {
return null;
}
const {
username,
} = this.state;
return (
<form id={formId} onSubmit={this.handleSubmit}>
<LineInput
autoComplete="username email"
id="username"
placeholder={intl.formatMessage(messages.username)}
type="text"
value={username}
api={this.handleUsernameInputApi}
onChange={this.handleUsernameChange}
/>
<PasswordInput
autoComplete="current-password"
id="password"
placeholder={intl.formatMessage(messages.password)}
api={this.handlePasswordInputApi}
/>
{this.renderButtonBar()}
</form>
);
}
renderError() {
const {
error,
isPending,
} = this.props;
if (isPending || !error) {
return undefined;
}
const messageKey = error.get('code') || 'error';
return (
<Notification
id="loginForm.error"
items={[{
message: messages[messageKey],
}]}
showCloseButton={false}
status="error"
/>
);
}
render() {
const {
isPending,
isSuccess,
} = this.props;
let className;
if (isPending) {
className = styles.pending;
} else if (isSuccess) {
className = styles.success;
} else {
className = styles.common;
}
return (
<div className={className}>
{this.renderHeader()}
{this.renderPrompt()}
{this.renderForm()}
{this.renderError()}
</div>
);
}
}
LoginForm.propTypes = propTypes;
LoginForm.defaultProps = defaultProps;
LoginForm.contextTypes = contextTypes;
export default injectIntl(LoginForm);