fix: phone input validation error should display red borders like email input (#13275)

ISSUE 

- closes #13032

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Nabhag Motivaras
2025-07-23 19:22:41 +05:30
committed by GitHub
parent 186891883e
commit 1e5d2f9b21
3 changed files with 45 additions and 2 deletions

View File

@ -78,6 +78,7 @@ export type MultiItemBaseInputProps = Omit<HTMLInputProps, 'onChange'> & {
onChange: (value: string) => void;
autoFocus: HTMLInputProps['autoFocus'];
placeholder: HTMLInputProps['placeholder'];
hasError?: boolean;
}) => React.ReactNode;
error?: string | null;
hasError?: boolean;
@ -134,6 +135,7 @@ export const MultiItemBaseInput = forwardRef<
onChange,
autoFocus,
placeholder,
hasError,
})
) : (
<StyledInput

View File

@ -1,5 +1,8 @@
import { usePhonesField } from '@/object-record/record-field/meta-types/hooks/usePhonesField';
import { PhonesFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/PhonesFieldMenuItem';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
import { phoneSchema } from '@/object-record/record-field/validation-schemas/phoneSchema';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import styled from '@emotion/styled';
import { E164Number, parsePhoneNumber } from 'libphonenumber-js';
import ReactPhoneNumberInput from 'react-phone-number-input';
@ -19,6 +22,7 @@ export const DEFAULT_PHONE_CALLING_CODE = '1';
const StyledCustomPhoneInputContainer = styled.div<{
hasItem: boolean;
hasError?: boolean;
}>`
${({ hasItem, theme }) =>
hasItem &&
@ -28,6 +32,13 @@ const StyledCustomPhoneInputContainer = styled.div<{
border: 1px solid ${theme.border.color.medium};
height: 30px;
`}
${({ hasError, hasItem, theme }) =>
hasError &&
hasItem &&
css`
border: 1px solid ${theme.border.color.danger};
`}
`;
const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
@ -93,10 +104,23 @@ export const PhonesFieldInput = ({
});
};
const validateInput = (input: string) => ({
isValid: phoneSchema.safeParse(input).success,
errorMessage: '',
});
const getShowPrimaryIcon = (index: number) =>
index === 0 && phones.length > 1;
const getShowSetAsPrimaryButton = (index: number) => index > 0;
const setIsFieldInError = useSetRecoilComponentStateV2(
recordFieldInputIsFieldInErrorComponentState,
);
const handleError = (hasError: boolean, values: any[]) => {
setIsFieldInError(hasError && values.length === 0);
};
return (
<MultiItemFieldInput
items={phones}
@ -105,6 +129,7 @@ export const PhonesFieldInput = ({
onCancel={onCancel}
placeholder="Phone"
fieldMetadataType={FieldMetadataType.PHONES}
validateInput={validateInput}
formatInput={(input) => {
const phone = parsePhoneNumber(input);
if (phone !== undefined) {
@ -138,9 +163,12 @@ export const PhonesFieldInput = ({
onDelete={handleDelete}
/>
)}
renderInput={({ value, onChange, autoFocus, placeholder }) => {
renderInput={({ value, onChange, autoFocus, placeholder, hasError }) => {
return (
<StyledCustomPhoneInputContainer hasItem={!!phones.length}>
<StyledCustomPhoneInputContainer
hasItem={!!phones.length}
hasError={hasError}
>
<StyledCustomPhoneInput
autoFocus={autoFocus}
placeholder={placeholder}
@ -154,6 +182,7 @@ export const PhonesFieldInput = ({
</StyledCustomPhoneInputContainer>
);
}}
onError={handleError}
/>
);
};

View File

@ -0,0 +1,12 @@
import { parsePhoneNumberWithError } from 'libphonenumber-js';
import { z } from 'zod';
export const phoneSchema = z.string().refine((value) => {
if (!value || value.trim() === '') return false;
try {
const phone = parsePhoneNumberWithError(value);
return phone.isValid();
} catch {
return false;
}
});