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:
@ -78,6 +78,7 @@ export type MultiItemBaseInputProps = Omit<HTMLInputProps, 'onChange'> & {
|
|||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
autoFocus: HTMLInputProps['autoFocus'];
|
autoFocus: HTMLInputProps['autoFocus'];
|
||||||
placeholder: HTMLInputProps['placeholder'];
|
placeholder: HTMLInputProps['placeholder'];
|
||||||
|
hasError?: boolean;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
error?: string | null;
|
error?: string | null;
|
||||||
hasError?: boolean;
|
hasError?: boolean;
|
||||||
@ -134,6 +135,7 @@ export const MultiItemBaseInput = forwardRef<
|
|||||||
onChange,
|
onChange,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
hasError,
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<StyledInput
|
<StyledInput
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { usePhonesField } from '@/object-record/record-field/meta-types/hooks/usePhonesField';
|
import { usePhonesField } from '@/object-record/record-field/meta-types/hooks/usePhonesField';
|
||||||
import { PhonesFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/PhonesFieldMenuItem';
|
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 styled from '@emotion/styled';
|
||||||
import { E164Number, parsePhoneNumber } from 'libphonenumber-js';
|
import { E164Number, parsePhoneNumber } from 'libphonenumber-js';
|
||||||
import ReactPhoneNumberInput from 'react-phone-number-input';
|
import ReactPhoneNumberInput from 'react-phone-number-input';
|
||||||
@ -19,6 +22,7 @@ export const DEFAULT_PHONE_CALLING_CODE = '1';
|
|||||||
|
|
||||||
const StyledCustomPhoneInputContainer = styled.div<{
|
const StyledCustomPhoneInputContainer = styled.div<{
|
||||||
hasItem: boolean;
|
hasItem: boolean;
|
||||||
|
hasError?: boolean;
|
||||||
}>`
|
}>`
|
||||||
${({ hasItem, theme }) =>
|
${({ hasItem, theme }) =>
|
||||||
hasItem &&
|
hasItem &&
|
||||||
@ -28,6 +32,13 @@ const StyledCustomPhoneInputContainer = styled.div<{
|
|||||||
border: 1px solid ${theme.border.color.medium};
|
border: 1px solid ${theme.border.color.medium};
|
||||||
height: 30px;
|
height: 30px;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
|
${({ hasError, hasItem, theme }) =>
|
||||||
|
hasError &&
|
||||||
|
hasItem &&
|
||||||
|
css`
|
||||||
|
border: 1px solid ${theme.border.color.danger};
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
|
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) =>
|
const getShowPrimaryIcon = (index: number) =>
|
||||||
index === 0 && phones.length > 1;
|
index === 0 && phones.length > 1;
|
||||||
const getShowSetAsPrimaryButton = (index: number) => index > 0;
|
const getShowSetAsPrimaryButton = (index: number) => index > 0;
|
||||||
|
|
||||||
|
const setIsFieldInError = useSetRecoilComponentStateV2(
|
||||||
|
recordFieldInputIsFieldInErrorComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleError = (hasError: boolean, values: any[]) => {
|
||||||
|
setIsFieldInError(hasError && values.length === 0);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultiItemFieldInput
|
<MultiItemFieldInput
|
||||||
items={phones}
|
items={phones}
|
||||||
@ -105,6 +129,7 @@ export const PhonesFieldInput = ({
|
|||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
placeholder="Phone"
|
placeholder="Phone"
|
||||||
fieldMetadataType={FieldMetadataType.PHONES}
|
fieldMetadataType={FieldMetadataType.PHONES}
|
||||||
|
validateInput={validateInput}
|
||||||
formatInput={(input) => {
|
formatInput={(input) => {
|
||||||
const phone = parsePhoneNumber(input);
|
const phone = parsePhoneNumber(input);
|
||||||
if (phone !== undefined) {
|
if (phone !== undefined) {
|
||||||
@ -138,9 +163,12 @@ export const PhonesFieldInput = ({
|
|||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
renderInput={({ value, onChange, autoFocus, placeholder }) => {
|
renderInput={({ value, onChange, autoFocus, placeholder, hasError }) => {
|
||||||
return (
|
return (
|
||||||
<StyledCustomPhoneInputContainer hasItem={!!phones.length}>
|
<StyledCustomPhoneInputContainer
|
||||||
|
hasItem={!!phones.length}
|
||||||
|
hasError={hasError}
|
||||||
|
>
|
||||||
<StyledCustomPhoneInput
|
<StyledCustomPhoneInput
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
@ -154,6 +182,7 @@ export const PhonesFieldInput = ({
|
|||||||
</StyledCustomPhoneInputContainer>
|
</StyledCustomPhoneInputContainer>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
onError={handleError}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user