0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

Merge pull request #3096 from logto-io/simeng-fix-react-hook-on-blur

fix(ui): avoid onBlur event and set input field value from react-hook-form triggered
This commit is contained in:
simeng-li 2023-02-14 16:37:51 +08:00 committed by GitHub
commit 087935cfd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 51 deletions

View file

@ -16,7 +16,7 @@ import { getInputHtmlProps } from './utils';
export type { IdentifierInputType, EnabledIdentifierTypes } from './use-smart-input-field'; export type { IdentifierInputType, EnabledIdentifierTypes } from './use-smart-input-field';
type Props = Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'prefix'> & { type Props = Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'prefix' | 'value'> & {
className?: string; className?: string;
errorMessage?: string; errorMessage?: string;
isDanger?: boolean; isDanger?: boolean;
@ -24,12 +24,12 @@ type Props = Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'prefix'> & {
enabledTypes?: EnabledIdentifierTypes; enabledTypes?: EnabledIdentifierTypes;
currentType?: IdentifierInputType; currentType?: IdentifierInputType;
onTypeChange?: (type: IdentifierInputType) => void; onTypeChange?: (type: IdentifierInputType) => void;
value?: string;
onChange?: (value: string) => void; onChange?: (value: string) => void;
}; };
const SmartInputField = ( const SmartInputField = (
{ {
value,
onChange, onChange,
currentType = SignInIdentifier.Username, currentType = SignInIdentifier.Username,
enabledTypes = [currentType], enabledTypes = [currentType],

View file

@ -1,7 +1,7 @@
import { SignInIdentifier } from '@logto/schemas'; import { SignInIdentifier } from '@logto/schemas';
import classNames from 'classnames'; import classNames from 'classnames';
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Button from '@/components/Button'; import Button from '@/components/Button';
@ -36,13 +36,12 @@ const IdentifierRegisterForm = ({ className, autoFocus, signUpMethods }: Props)
const { errorMessage, clearErrorMessage, onSubmit } = useOnSubmit(); const { errorMessage, clearErrorMessage, onSubmit } = useOnSubmit();
const { const {
register,
setValue, setValue,
handleSubmit, handleSubmit,
formState: { errors, isSubmitted }, formState: { errors, isSubmitted },
control,
} = useForm<FormState>({ } = useForm<FormState>({
reValidateMode: 'onChange', reValidateMode: 'onChange',
defaultValues: { identifier: '' },
}); });
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
@ -62,17 +61,10 @@ const IdentifierRegisterForm = ({ className, autoFocus, signUpMethods }: Props)
return ( return (
<form className={classNames(styles.form, className)} onSubmit={onSubmitHandler}> <form className={classNames(styles.form, className)} onSubmit={onSubmitHandler}>
<SmartInputField <Controller
required control={control}
autoComplete="new-identifier" name="identifier"
autoFocus={autoFocus} rules={{
className={styles.inputField}
currentType={inputType}
isDanger={!!errors.identifier || !!errorMessage}
errorMessage={errors.identifier?.message}
enabledTypes={signUpMethods}
onTypeChange={setInputType}
{...register('identifier', {
required: getGeneralIdentifierErrorMessage(signUpMethods, 'required'), required: getGeneralIdentifierErrorMessage(signUpMethods, 'required'),
validate: (value) => { validate: (value) => {
const errorMessage = validateIdentifierField(inputType, value); const errorMessage = validateIdentifierField(inputType, value);
@ -85,11 +77,24 @@ const IdentifierRegisterForm = ({ className, autoFocus, signUpMethods }: Props)
return true; return true;
}, },
})}
/* Overwrite default input onChange handler */
onChange={(value) => {
setValue('identifier', value, { shouldValidate: isSubmitted, shouldDirty: true });
}} }}
render={({ field }) => (
<SmartInputField
autoComplete="new-identifier"
autoFocus={autoFocus}
className={styles.inputField}
{...field}
currentType={inputType}
isDanger={!!errors.identifier || !!errorMessage}
errorMessage={errors.identifier?.message}
enabledTypes={signUpMethods}
onTypeChange={setInputType}
/* Overwrite default input onChange handler */
onChange={(value) => {
setValue('identifier', value, { shouldValidate: isSubmitted, shouldDirty: true });
}}
/>
)}
/> />
{errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>} {errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>}

View file

@ -2,7 +2,7 @@ import { SignInIdentifier } from '@logto/schemas';
import type { SignIn } from '@logto/schemas'; import type { SignIn } from '@logto/schemas';
import classNames from 'classnames'; import classNames from 'classnames';
import { useState, useCallback, useMemo } from 'react'; import { useState, useCallback, useMemo } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import Button from '@/components/Button'; import Button from '@/components/Button';
import ErrorMessage from '@/components/ErrorMessage'; import ErrorMessage from '@/components/ErrorMessage';
@ -40,9 +40,9 @@ const IdentifierSignInForm = ({ className, autoFocus, signInMethods }: Props) =>
); );
const { const {
register,
setValue, setValue,
handleSubmit, handleSubmit,
control,
formState: { errors, isSubmitted }, formState: { errors, isSubmitted },
} = useForm<FormState>({ } = useForm<FormState>({
reValidateMode: 'onChange', reValidateMode: 'onChange',
@ -68,16 +68,10 @@ const IdentifierSignInForm = ({ className, autoFocus, signInMethods }: Props) =>
return ( return (
<form className={classNames(styles.form, className)} onSubmit={onSubmitHandler}> <form className={classNames(styles.form, className)} onSubmit={onSubmitHandler}>
<SmartInputField <Controller
autoComplete="new-identifier" control={control}
autoFocus={autoFocus} name="identifier"
className={styles.inputField} rules={{
currentType={inputType}
isDanger={!!errors.identifier || !!errorMessage}
errorMessage={errors.identifier?.message}
enabledTypes={enabledSignInMethods}
onTypeChange={setInputType}
{...register('identifier', {
required: getGeneralIdentifierErrorMessage(enabledSignInMethods, 'required'), required: getGeneralIdentifierErrorMessage(enabledSignInMethods, 'required'),
validate: (value) => { validate: (value) => {
const errorMessage = validateIdentifierField(inputType, value); const errorMessage = validateIdentifierField(inputType, value);
@ -86,11 +80,24 @@ const IdentifierSignInForm = ({ className, autoFocus, signInMethods }: Props) =>
? getGeneralIdentifierErrorMessage(enabledSignInMethods, 'invalid') ? getGeneralIdentifierErrorMessage(enabledSignInMethods, 'invalid')
: true; : true;
}, },
})}
/* Overwrite default input onChange handler */
onChange={(value) => {
setValue('identifier', value, { shouldValidate: isSubmitted, shouldDirty: true });
}} }}
render={({ field }) => (
<SmartInputField
autoComplete="identifier"
autoFocus={autoFocus}
className={styles.inputField}
{...field}
currentType={inputType}
isDanger={!!errors.identifier || !!errorMessage}
errorMessage={errors.identifier?.message}
enabledTypes={enabledSignInMethods}
onTypeChange={setInputType}
/* Overwrite default input onChange handler */
onChange={(value) => {
setValue('identifier', value, { shouldValidate: isSubmitted, shouldDirty: true });
}}
/>
)}
/> />
{errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>} {errorMessage && <ErrorMessage className={styles.formErrors}>{errorMessage}</ErrorMessage>}

View file

@ -1,7 +1,7 @@
import { SignInIdentifier } from '@logto/schemas'; import { SignInIdentifier } from '@logto/schemas';
import classNames from 'classnames'; import classNames from 'classnames';
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { useForm } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Button from '@/components/Button'; import Button from '@/components/Button';
@ -46,6 +46,7 @@ const PasswordSignInForm = ({ className, autoFocus, signInMethods }: Props) => {
register, register,
setValue, setValue,
handleSubmit, handleSubmit,
control,
formState: { errors, isSubmitted }, formState: { errors, isSubmitted },
} = useForm<FormState>({ } = useForm<FormState>({
reValidateMode: 'onChange', reValidateMode: 'onChange',
@ -72,27 +73,34 @@ const PasswordSignInForm = ({ className, autoFocus, signInMethods }: Props) => {
return ( return (
<form className={classNames(styles.form, className)} onSubmit={onSubmitHandler}> <form className={classNames(styles.form, className)} onSubmit={onSubmitHandler}>
<SmartInputField <Controller
autoComplete="identifier" control={control}
autoFocus={autoFocus} name="identifier"
className={styles.inputField} rules={{
currentType={inputType}
isDanger={!!errors.identifier}
errorMessage={errors.identifier?.message}
enabledTypes={signInMethods}
onTypeChange={setInputType}
{...register('identifier', {
required: getGeneralIdentifierErrorMessage(signInMethods, 'required'), required: getGeneralIdentifierErrorMessage(signInMethods, 'required'),
validate: (value) => { validate: (value) => {
const errorMessage = validateIdentifierField(inputType, value); const errorMessage = validateIdentifierField(inputType, value);
return errorMessage ? getGeneralIdentifierErrorMessage(signInMethods, 'invalid') : true; return errorMessage ? getGeneralIdentifierErrorMessage(signInMethods, 'invalid') : true;
}, },
})}
/* Overwrite default input onChange handler */
onChange={(value) => {
setValue('identifier', value, { shouldValidate: isSubmitted, shouldDirty: true });
}} }}
render={({ field }) => (
<SmartInputField
autoComplete="identifier"
autoFocus={autoFocus}
className={styles.inputField}
{...field}
currentType={inputType}
isDanger={!!errors.identifier}
errorMessage={errors.identifier?.message}
enabledTypes={signInMethods}
onTypeChange={setInputType}
/* Overwrite default input onChange handler */
onChange={(value) => {
setValue('identifier', value, { shouldValidate: isSubmitted, shouldDirty: true });
}}
/>
)}
/> />
<PasswordInputField <PasswordInputField

View file

@ -97,3 +97,18 @@ export const formatPhoneNumberWithCountryCallingCode = (number: string) => {
return number; return number;
} }
}; };
export const parsePhoneNumber = (value: string) => {
try {
const phoneNumber = parsePhoneNumberWithError(parseE164Number(value));
return {
countryCallingCode: phoneNumber.countryCallingCode,
nationalNumber: phoneNumber.nationalNumber,
};
} catch {
return {
nationalNumber: value,
};
}
};