omeralpi / shadcn-phone-input

Customizable phone input component with proper validation for any country. Built on top of shadcn.
https://shadcn-phone-input.vercel.app
MIT License
422 stars 28 forks source link

Types of property 'value' are incompatible. Type 'string' is not assignable to type 'E164Number' #28

Closed Yokohmariam closed 1 week ago

Yokohmariam commented 3 months ago

First this is awesome i love it.

But when i try to use the example code The input form is throwing this error

Type '{ onChange: (...event: any[]) => void; onBlur: Noop; value: string; disabled?: boolean | undefined; name: "phone"; ref: RefCallBack; placeholder: string; }' is not assignable to type 'Omit<Props<PhoneInputWithCountrySelectType>, "onChange">'. Types of property 'value' are incompatible. Type 'string' is not assignable to type 'E164Number'.ts(2322) (alias) const PhoneInput: React.ForwardRefExoticComponent import PhoneInput

Screenshot 2024-05-11 at 8 14 46 in the evening
thallysondias commented 3 months ago

+1

alex-dnr commented 3 months ago

My mistake was that my editor auto imported Form component from react-hook-form when it should have been imported from @/components/ui/form. 👀

Yokohmariam commented 3 months ago

still am getting same error

femiorok commented 3 months ago

Added some basic type conversion and it worked for me.

onChange={(value) => onChange?.(value || ('' as RPNInput.Value))}

owencage commented 3 months ago

still am getting same error

Did you manage to get it sorted?

Yokohmariam commented 3 months ago

still am getting same error

Did you manage to get it sorted? i have built new from scratch to use with react-hook-form

this is the phone-input component ready to use with react-hook-form


import "react-phone-number-input/style.css";
import * as RPNInput from "react-phone-number-input";
import { forwardRef, useCallback } from "react";
import { Control, FieldValues, Path } from "react-hook-form";

import flags from "react-phone-number-input/flags";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "./ui/button";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import { cn } from "@/lib/utils";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command";
import { ScrollArea } from "./ui/scroll-area";
import { InputProps } from "./ui/input";
import {
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "./ui/form";

interface PhoneProps<TFormValues extends FieldValues> {
  name: Path<TFormValues>;
  control: Control<TFormValues> | undefined;
  label: string;
  placeholder: string;
}

const Phone = <TFormValues extends FieldValues>({
  name,
  control,
  label,
  placeholder,
}: PhoneProps<TFormValues>) => {
  return (
    <FormField
      control={control}
      name={name}
      render={({ field }) => (
        <FormItem>
          <FormLabel className="text-kblack-text text-[16px]" htmlFor={name}>
            {label}
          </FormLabel>
          <FormControl>
            <RPNInput.default
              defaultCountry="ET"
              className={cn("flex")}
              flagComponent={FlagComponent}
              countrySelectComponent={CountrySelect}
              inputComponent={InputComponent}
              placeholder={placeholder}
              {...field}
            />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  );
};

export default Phone;

const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
  const Flag = flags[country];

  return (
    <span className="flex h-4 w-6 overflow-hidden rounded-sm ">
      {Flag && <Flag title={countryName} />}
    </span>
  );
};
FlagComponent.displayName = "FlagComponent";

type CountrySelectOption = { label: string; value: RPNInput.Country };

type CountrySelectProps = {
  disabled?: boolean;
  value: RPNInput.Country;
  onChange: (value: RPNInput.Country) => void;
  options: CountrySelectOption[];
};

const CountrySelect = ({
  disabled,
  value,
  onChange,
  options,
}: CountrySelectProps) => {
  const handleSelect = useCallback(
    (country: RPNInput.Country) => {
      onChange(country);
    },
    [onChange]
  );

  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button
          type="button"
          variant={"outline"}
          className={cn(
            "flex gap-1 rounded-e-none rounded-s-lg px-3 bg-kinput-bg border-0 py-6"
          )}
          disabled={disabled}
        >
          <FlagComponent country={value} countryName={value} />
          <ChevronsUpDown
            className={cn(
              "-mr-2 h-4 w-4 opacity-50",
              disabled ? "hidden" : "opacity-100"
            )}
          />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-[300px] p-0">
        <Command>
          <CommandList>
            <ScrollArea className="h-72">
              <CommandInput placeholder="Search country..." />
              <CommandEmpty>No country found.</CommandEmpty>
              <CommandGroup>
                {options
                  .filter((x) => x.value)
                  .map((option) => (
                    <CommandItem
                      className="gap-2"
                      key={option.value}
                      onSelect={() => handleSelect(option.value)}
                    >
                      <FlagComponent
                        country={option.value}
                        countryName={option.label}
                      />
                      <span className="flex-1 text-sm">{option.label}</span>
                      {option.value && (
                        <span className="text-sm text-foreground/50">
                          {`+${RPNInput.getCountryCallingCode(option.value)}`}
                        </span>
                      )}
                      <CheckIcon
                        className={cn(
                          "ml-auto h-4 w-4",
                          option.value === value ? "opacity-100" : "opacity-0"
                        )}
                      />
                    </CommandItem>
                  ))}
              </CommandGroup>
            </ScrollArea>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
};

const InputComponent = forwardRef<HTMLInputElement, InputProps>(
  ({ className, ...props }, ref) => (
    <input
      name={props.name}
      className="outline-none rounded-r-md w-full  px-4 py-3 bg-kinput-bg flex-initial"
      placeholder="Phone number"
      {...props}
      ref={ref}
    />
  )
);
InputComponent.displayName = "InputComponent";

so you can use it like this easly inside your component

<Phone
          name={"phone"}
          control={form.control}
          label={"Phone Number"}
          placeholder={"Phone number"}
        />

make your sure to include the phone validation

 import { isValidPhoneNumber } from "react-phone-number-input";
   phone: z
      .string()
      .refine(isValidPhoneNumber, { message: "Invalid phone number" }), 
abuu-u commented 3 months ago
onChange={(value) =>
  onChange?.(
    value ||
      Object.assign<"", { __tag: "E164Number" }>("", {
        __tag: "E164Number",
      })
  )
}
jjppsia commented 3 months ago

I encountered the same type of error. I believe it's due to the fact that the react-phone-number-input package utilizes the latest version of libphonenumber-js, which has a different type value for E164Number.

Screenshot 2024-05-14 212241

To address this issue temporarily, I included libphonenumber-js as a dependency and specified its version as 1.10.61 to revert to the previous type value for E164Number.

Screenshot 2024-05-14 212547

1Mouse commented 3 months ago

@jjppsia yeah this version is supposed to solve the issue I saw the channel log on gitlab how did you manage to force react-phone-number-input 3.4.1 to use libphonenumber-js 1.10.61 ?

1Mouse commented 3 months ago

@jjppsia yeah this version is supposed to solve the issue I saw the channel log on gitlab how did you manage to force react-phone-number-input 3.4.1 to use libphonenumber-js 1.10.61 ?

I tried this but react-phone-number-input still uses version 1.11.1

  "overrides": {
    "react-phone-number-input": {
      "dependencies": {
        "libphonenumber-js": "1.10.61"
      }
    }
  },
jjppsia commented 3 months ago

oh i npm i libphonenumber-js@1.10.61 and deleted both my node_modules and package-lock.json and installed them again.

1Mouse commented 3 months ago

@jjppsia thanks for the quick response

overrides doesn't work with yarn so I used resolutions and everything now works

  "resolutions": {
    "react-phone-number-input/libphonenumber-js": "1.10.61"
  }
ezevic commented 3 months ago

@jjppsia thanks for the quick response

overrides doesn't work with yarn so I used resolutions and everything now works

  "resolutions": {
    "react-phone-number-input/libphonenumber-js": "1.10.61"
  }

does not work for me

1Mouse commented 3 months ago

@ezevic try deleting node modules besides the resolution

ezevic commented 3 months ago

@ezevic try deleting node modules besides the resolution

I did a clean install of the project's dependencies. I also checked the libphonenumber-js package version and it is 1.10.61

1Mouse commented 3 months ago

@ezevic try deleting node modules besides the resolution

I did a clean install of the project's dependencies. I also checked the libphonenumber-js package version and it is 1.10.61

u checked it in your package json or in the package json of react-phone-number-input in node_modules? u need to make sure that installed version of react-phone-number-input uses libphonenumber-js 1.10.61 as it could be 1.10.61 in your package json but the react-phone-number-input uses another version behind the scenes

ezevic commented 3 months ago

@ezevic try deleting node modules besides the resolution

I did a clean install of the project's dependencies. I also checked the libphonenumber-js package version and it is 1.10.61

u checked it in your package json or in the package json of react-phone-number-input in node_modules? u need to make sure that installed version of react-phone-number-input uses libphonenumber-js 1.10.61 as it could be 1.10.61 in your package json but the react-phone-number-input uses another version behind the scenes

I checked in the package json of react-phone-number-input