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:
commit
087935cfd3
5 changed files with 86 additions and 51 deletions
|
@ -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],
|
||||||
|
|
|
@ -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>}
|
||||||
|
|
|
@ -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>}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue