UNPKG

cspace-ui

Version:
368 lines (312 loc) 8.33 kB
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);