add focus & fix styles for auth forms errors (#156)

* add focus & fix styles for auth forms errors

* refactor by review comments

* minor changes: let ref -> const ref: { current }, additional typings

---------

Co-authored-by: bniwredyc <bniwredyc@gmail.com>
This commit is contained in:
Arkadzi Rakouski 2023-08-13 16:36:18 +03:00 committed by GitHub
parent 8e5f34eb85
commit 4fea17a2ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 115 additions and 55 deletions

View File

@ -28,6 +28,8 @@ export const ForgotPasswordForm = () => {
const [validationErrors, setValidationErrors] = createSignal<ValidationErrors>({}) const [validationErrors, setValidationErrors] = createSignal<ValidationErrors>({})
const [isUserNotFount, setIsUserNotFound] = createSignal(false) const [isUserNotFount, setIsUserNotFound] = createSignal(false)
const authFormRef: { current: HTMLFormElement } = { current: null }
const handleSubmit = async (event: Event) => { const handleSubmit = async (event: Event) => {
event.preventDefault() event.preventDefault()
@ -47,6 +49,10 @@ export const ForgotPasswordForm = () => {
const isValid = Object.keys(newValidationErrors).length === 0 const isValid = Object.keys(newValidationErrors).length === 0
if (!isValid) { if (!isValid) {
authFormRef.current
.querySelector<HTMLInputElement>(`input[name="${Object.keys(newValidationErrors)[0]}"]`)
.focus()
return return
} }
@ -66,7 +72,11 @@ export const ForgotPasswordForm = () => {
} }
return ( return (
<form onSubmit={handleSubmit} class={clsx(styles.authForm, styles.authFormForgetPassword)}> <form
onSubmit={handleSubmit}
class={clsx(styles.authForm, styles.authFormForgetPassword)}
ref={(el) => (authFormRef.current = el)}
>
<div> <div>
<h4>{t('Forgot password?')}</h4> <h4>{t('Forgot password?')}</h4>
<div class={styles.authSubtitle}>{t('Everything is ok, please give us your email address')}</div> <div class={styles.authSubtitle}>{t('Everything is ok, please give us your email address')}</div>
@ -95,7 +105,11 @@ export const ForgotPasswordForm = () => {
<Show when={validationErrors().email}> <Show when={validationErrors().email}>
<div class={styles.validationError}>{validationErrors().email}</div> <div class={styles.validationError}>{validationErrors().email}</div>
</Show> </Show>
<div class="pretty-form__item"> <div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().email
})}
>
<input <input
id="email" id="email"
name="email" name="email"

View File

@ -33,6 +33,8 @@ export const LoginForm = () => {
const [isLinkSent, setIsLinkSent] = createSignal(false) const [isLinkSent, setIsLinkSent] = createSignal(false)
const [showPassword, setShowPassword] = createSignal(false) const [showPassword, setShowPassword] = createSignal(false)
const authFormRef: { current: HTMLFormElement } = { current: null }
const { const {
actions: { showSnackbar } actions: { showSnackbar }
} = useSnackbar() } = useSnackbar()
@ -57,9 +59,11 @@ export const LoginForm = () => {
const handleSendLinkAgainClick = async (event: Event) => { const handleSendLinkAgainClick = async (event: Event) => {
event.preventDefault() event.preventDefault()
setIsLinkSent(true)
setIsEmailNotConfirmed(false) setIsEmailNotConfirmed(false)
setSubmitError('') setSubmitError('')
setIsLinkSent(true)
const result = await signSendLink({ email: email(), lang: lang(), template: 'email_confirmation' }) const result = await signSendLink({ email: email(), lang: lang(), template: 'email_confirmation' })
if (result.error) setSubmitError(result.error) if (result.error) setSubmitError(result.error)
} }
@ -85,6 +89,11 @@ export const LoginForm = () => {
if (Object.keys(newValidationErrors).length > 0) { if (Object.keys(newValidationErrors).length > 0) {
setValidationErrors(newValidationErrors) setValidationErrors(newValidationErrors)
authFormRef.current
.querySelector<HTMLInputElement>(`input[name="${Object.keys(newValidationErrors)[0]}"]`)
.focus()
return return
} }
@ -92,18 +101,23 @@ export const LoginForm = () => {
try { try {
await signIn({ email: email(), password: password() }) await signIn({ email: email(), password: password() })
hideModal() hideModal()
showSnackbar({ body: t('Welcome!') }) showSnackbar({ body: t('Welcome!') })
} catch (error) { } catch (error) {
if (error instanceof ApiError) { if (error instanceof ApiError) {
if (error.code === 'email_not_confirmed') { if (error.code === 'email_not_confirmed') {
setSubmitError(t('Please, confirm email')) setSubmitError(t('Please, confirm email'))
setIsEmailNotConfirmed(true) setIsEmailNotConfirmed(true)
return return
} }
if (error.code === 'user_not_found') { if (error.code === 'user_not_found') {
setSubmitError(t('Something went wrong, check email and password')) setSubmitError(t('Something went wrong, check email and password'))
return return
} }
} }
@ -115,7 +129,7 @@ export const LoginForm = () => {
} }
return ( return (
<form onSubmit={handleSubmit} class={styles.authForm}> <form onSubmit={handleSubmit} class={styles.authForm} ref={(el) => (authFormRef.current = el)}>
<div> <div>
<AuthModalHeader modalType="login" /> <AuthModalHeader modalType="login" />
<Show when={submitError()}> <Show when={submitError()}>
@ -131,7 +145,11 @@ export const LoginForm = () => {
<Show when={isLinkSent()}> <Show when={isLinkSent()}>
<div class={styles.authInfo}>{t('Link sent, check your email')}</div> <div class={styles.authInfo}>{t('Link sent, check your email')}</div>
</Show> </Show>
<div class="pretty-form__item"> <div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().email
})}
>
<input <input
id="email" id="email"
name="email" name="email"
@ -147,7 +165,11 @@ export const LoginForm = () => {
<div class={styles.validationError}>{validationErrors().email}</div> <div class={styles.validationError}>{validationErrors().email}</div>
</Show> </Show>
<div class="pretty-form__item"> <div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().password
})}
>
<input <input
id="password" id="password"
name="password" name="password"

View File

@ -15,7 +15,7 @@ import { validateEmail } from '../../../utils/validateEmail'
import { AuthModalHeader } from './AuthModalHeader' import { AuthModalHeader } from './AuthModalHeader'
type FormFields = { type FormFields = {
name: string fullName: string
email: string email: string
password: string password: string
} }
@ -28,12 +28,14 @@ export const RegisterForm = () => {
const { emailChecks } = useEmailChecks() const { emailChecks } = useEmailChecks()
const [submitError, setSubmitError] = createSignal('') const [submitError, setSubmitError] = createSignal('')
const [name, setName] = createSignal('') const [fullName, setFullName] = createSignal('')
const [password, setPassword] = createSignal('') const [password, setPassword] = createSignal('')
const [isSubmitting, setIsSubmitting] = createSignal(false) const [isSubmitting, setIsSubmitting] = createSignal(false)
const [isSuccess, setIsSuccess] = createSignal(false) const [isSuccess, setIsSuccess] = createSignal(false)
const [validationErrors, setValidationErrors] = createSignal<ValidationErrors>({}) const [validationErrors, setValidationErrors] = createSignal<ValidationErrors>({})
const authFormRef: { current: HTMLFormElement } = { current: null }
const handleEmailInput = (newEmail: string) => { const handleEmailInput = (newEmail: string) => {
setValidationErrors(({ email: _notNeeded, ...rest }) => rest) setValidationErrors(({ email: _notNeeded, ...rest }) => rest)
setEmail(newEmail) setEmail(newEmail)
@ -73,8 +75,8 @@ export const RegisterForm = () => {
} }
const handleNameInput = (newPasswordCopy: string) => { const handleNameInput = (newPasswordCopy: string) => {
setValidationErrors(({ name: _notNeeded, ...rest }) => rest) setValidationErrors(({ fullName: _notNeeded, ...rest }) => rest)
setName(newPasswordCopy) setFullName(newPasswordCopy)
} }
const handleSubmit = async (event: Event) => { const handleSubmit = async (event: Event) => {
@ -84,11 +86,11 @@ export const RegisterForm = () => {
const newValidationErrors: ValidationErrors = {} const newValidationErrors: ValidationErrors = {}
const cleanName = name().trim() const cleanName = fullName().trim()
const cleanEmail = email().trim() const cleanEmail = email().trim()
if (!cleanName) { if (!cleanName) {
newValidationErrors.name = t('Please enter a name to sign your comments and publication') newValidationErrors.fullName = t('Please enter a name to sign your comments and publication')
} }
if (!cleanEmail) { if (!cleanEmail) {
@ -108,6 +110,10 @@ export const RegisterForm = () => {
const isValid = Object.keys(newValidationErrors).length === 0 && !emailCheckResult const isValid = Object.keys(newValidationErrors).length === 0 && !emailCheckResult
if (!isValid) { if (!isValid) {
authFormRef.current
.querySelector<HTMLInputElement>(`input[name="${Object.keys(newValidationErrors)[0]}"]`)
.focus()
return return
} }
@ -135,7 +141,7 @@ export const RegisterForm = () => {
return ( return (
<> <>
<Show when={!isSuccess()}> <Show when={!isSuccess()}>
<form onSubmit={handleSubmit} class={styles.authForm}> <form onSubmit={handleSubmit} class={styles.authForm} ref={(el) => (authFormRef.current = el)}>
<div> <div>
<AuthModalHeader modalType="register" /> <AuthModalHeader modalType="register" />
<Show when={submitError()}> <Show when={submitError()}>
@ -145,7 +151,11 @@ export const RegisterForm = () => {
</ul> </ul>
</div> </div>
</Show> </Show>
<div class="pretty-form__item"> <div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().fullName
})}
>
<input <input
name="fullName" name="fullName"
type="text" type="text"
@ -153,12 +163,16 @@ export const RegisterForm = () => {
autocomplete="" autocomplete=""
onInput={(event) => handleNameInput(event.currentTarget.value)} onInput={(event) => handleNameInput(event.currentTarget.value)}
/> />
<label for="fullName">{t('Full name')}</label> <label for="name">{t('Full name')}</label>
</div> </div>
<Show when={validationErrors().name}> <Show when={validationErrors().fullName}>
<div class={styles.validationError}>{validationErrors().name}</div> <div class={styles.validationError}>{validationErrors().fullName}</div>
</Show> </Show>
<div class="pretty-form__item"> <div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().email
})}
>
<input <input
id="email" id="email"
name="email" name="email"
@ -188,7 +202,11 @@ export const RegisterForm = () => {
</a> </a>
</div> </div>
</Show> </Show>
<div class="pretty-form__item"> <div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().password
})}
>
<input <input
id="password" id="password"
name="password" name="password"

View File

@ -391,42 +391,6 @@ button {
} }
form { form {
.pretty-form__item {
position: relative;
input {
padding-top: 1.4em;
}
textarea {
line-height: 1.4;
}
}
.pretty-form__item--with-button {
margin-bottom: 1.6rem;
@include media-breakpoint-up(sm) {
display: flex;
}
input {
flex: 1;
@include media-breakpoint-up(sm) {
margin-bottom: 0 !important;
}
}
*:first-child {
flex: 1;
@include media-breakpoint-up(sm) {
margin-right: 1em;
}
}
}
input[type='text'], input[type='text'],
input[type='email'], input[type='email'],
input[type='password'], input[type='password'],
@ -510,6 +474,48 @@ form {
.form-message { .form-message {
@include font-size(1.2rem); @include font-size(1.2rem);
} }
.pretty-form__item {
position: relative;
input {
padding-top: 1.4em;
}
textarea {
line-height: 1.4;
}
}
.pretty-form__item--error {
input {
border-color: #d00820;
}
}
.pretty-form__item--with-button {
margin-bottom: 1.6rem;
@include media-breakpoint-up(sm) {
display: flex;
}
input {
flex: 1;
@include media-breakpoint-up(sm) {
margin-bottom: 0 !important;
}
}
*:first-child {
flex: 1;
@include media-breakpoint-up(sm) {
margin-right: 1em;
}
}
}
} }
.input--short { .input--short {