Add functionality to LoginField component.

State management and error detection/animation added.
TBD: Connect fields to parent component.
This commit is contained in:
Daniel Scalzi 2020-05-23 02:03:20 -04:00
parent f1a7e39e13
commit c718cc741a

View File

@ -2,14 +2,59 @@ import * as React from 'react'
import './LoginField.css' import './LoginField.css'
type LoginFieldProps = { enum FieldError {
password: boolean, REQUIRED = 'Required',
INVALID = 'Invalid Value'
} }
export default class LoginField extends React.Component<LoginFieldProps> { type LoginFieldProps = {
password: boolean
}
getFieldSvg(): JSX.Element { type LoginFieldSettings = {
errorText: FieldError,
hasError: boolean,
shake: boolean,
value: string
}
export default class LoginField extends React.Component<LoginFieldProps, LoginFieldSettings> {
private readonly USERNAME_REGEX = /^[a-zA-Z0-9_]{1,16}$/
private readonly BASIC_EMAIL_REGEX = /^\S+@\S+\.\S+$/
// private readonly VALID_EMAIL_REGEX = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
private readonly SHAKE_CLASS = 'shake'
private errorSpanRef: React.RefObject<HTMLSpanElement>
constructor(props: LoginFieldProps) {
super(props)
this.state = {
errorText: FieldError.REQUIRED,
hasError: true,
shake: false,
value: ''
}
this.errorSpanRef = React.createRef()
}
componentDidUpdate() {
if(this.state.hasError) {
// @ts-ignore Opacity is a number, not a string..
this.errorSpanRef.current!.style.opacity = 1
if(this.state.shake) {
this.errorSpanRef.current!.classList.remove(this.SHAKE_CLASS)
void this.errorSpanRef.current!.offsetWidth
this.errorSpanRef.current!.classList.add(this.SHAKE_CLASS)
}
} else {
// @ts-ignore Opacity is a number, not a string..
this.errorSpanRef.current!.style.opacity = 0
}
}
private getFieldSvg(): JSX.Element {
if(this.props.password) { if(this.props.password) {
return ( return (
@ -37,29 +82,85 @@ export default class LoginField extends React.Component<LoginFieldProps> {
} }
} }
getDefaultErrorMessage(): string { private formatError(error: FieldError): string {
if(this.props.password) { return `* ${error}`
return '* Required' }
} else {
return '* Invalid Value' private getErrorState(shake: boolean, errorText: FieldError): Partial<LoginFieldSettings> {
return {
shake,
errorText,
hasError: true,
} }
} }
getPlaceholder(): string { private getValidState(): Partial<LoginFieldSettings> {
if(this.props.password) { return {
return 'PASSWORD' hasError: false
} else {
return 'EMAIL OR USERNAME'
} }
} }
private validateEmail = (value: string, shakeOnError: boolean): void => {
let newState
if(value) {
if(!this.BASIC_EMAIL_REGEX.test(value) && !this.USERNAME_REGEX.test(value)) {
newState = this.getErrorState(shakeOnError, FieldError.INVALID)
} else {
newState = this.getValidState()
}
} else {
newState = this.getErrorState(shakeOnError, FieldError.REQUIRED)
}
this.setState({
...this.state,
...newState,
value
})
}
private validatePassword = (value: string, shakeOnError: boolean): void => {
let newState
if(value) {
newState = this.getValidState()
} else {
newState = this.getErrorState(shakeOnError, FieldError.REQUIRED)
}
this.setState({
...this.state,
...newState,
value
})
}
private getValidateFunction(): (value: string, shakeOnError: boolean) => void {
return this.props.password ? this.validatePassword : this.validateEmail
}
private handleBlur = (event: React.FocusEvent<HTMLInputElement>): void => {
this.getValidateFunction()(event.target.value, true)
}
private handleInput = (event: React.FormEvent<HTMLInputElement>): void => {
this.getValidateFunction()((event.target as HTMLInputElement).value, false)
}
render() { render() {
return ( return (
<> <>
<div className="loginFieldContainer"> <div className="loginFieldContainer">
{this.getFieldSvg()} {this.getFieldSvg()}
<span className="loginErrorSpan">{this.getDefaultErrorMessage()}</span> <span
<input className="loginField" type={this.props.password ? 'password' : 'text'} placeholder={this.getPlaceholder()}/> className="loginErrorSpan"
ref={this.errorSpanRef}>
{this.formatError(this.state.errorText)}
</span>
<input
className="loginField"
type={this.props.password ? 'password' : 'text'}
value={this.state.value}
placeholder={this.props.password ? 'PASSWORD' : 'EMAIL OR USERNAME'}
onBlur={this.handleBlur}
onInput={this.handleInput} />
</div> </div>
</> </>
) )