patw0929 / react-intl-tel-input

Rewrite International Telephone Input in React.js. (Looking for maintainers, and PRs & contributors are also welcomed!)
https://patw0929.github.io/react-intl-tel-input/
MIT License
282 stars 221 forks source link

feat: new callback prop (renderComponent) to customize rendering #395

Open andrwo opened 2 years ago

andrwo commented 2 years ago

Adds a new renderProp to allow user to customize how to render the component.

Description

Adds a new prop to IntlTelInput:

{
  renderComponent? : (wrapperProps, flagDropDownProps, inputProps) => React.ReactNode
}

Currently, the rendering of the component is fixed with a <div> around <FlagDropDown ...> and <input>. If this callback is provided, instead of rendering the fixed default, will call the callback with the relevant props for user to customize their own rendering.

If callback not provided, will use the existing default rendering (<div ...> <FlagDropDown ...> <input ... >), so it does not affect any existing usages.

This allows users to customize using whatever Input component from any UI libraries (bootstrap, Material UI, etc.),. This enables this component to work smoothly with material UI (and other UI libraries) without messing around with the CSS to reproduce the UI's behaviour.

Will post CodeSandbox when I figure out how to link the module dependency to my fork...

Example usage (Material UI):

import InputAdornment from "@mui/material/InputAdornment";
import TextField from "@mui/material/TextField";
import IntlTelInput, { FlagDropDown } from "react-intl-tel-input";

<IntlTelInput
    value={phoneNumber}
    fieldName="phone"
    fieldId="phone_id"
    formatOnInit={true}
    autoHideDialCode={false}
    renderComponent={(wrapperProps, flagDropDownProps, inputProps) => {
      flagDropDownProps.flagContainerProps = { style: { position: "relative" } };
          inputProps.inputRef = inputProps.ref;
          inputProps.ref = undefined;
      inputProps.InputProps = {
        startAdornment: (
          <InputAdornment position="start">
            <div {...wrapperProps}>
              <FlagDropDown {...flagDropDownProps} />
            </div>
          </InputAdornment>
        ),
      };
      return (
        <TextField
          variant="filled"
          margin="dense"
          size="small"
          fullWidth
          {...inputProps}
        />
      );
    }}
    onPhoneNumberChange={(v, nn) => setPhoneNumber(nn)}
  />

Example usage (reactstrap (bootstrap React)):

import { Input, InputGroup, InputGroupAddon, InputGroupText } from "reactstrap";
import IntlTelInput, { FlagDropDown } from "react-intl-tel-input";

<IntlTelInput
    value={phoneNumber}
    fieldName="phone"
    fieldId="phone_id"
    formatOnInit={true}
    autoHideDialCode={false}
        renderComponent={(wrapperProps, flagDropDownProps, inputProps) => {
          flagDropDownProps.flagContainerProps = { style: { position: "relative" } };
          inputProps.innerRef = inputProps.ref;
          inputProps.ref = undefined;
          return (
            <InputGroup>
              <InputGroupAddon addonType="prepend">
                <InputGroupText>
                  <div {...wrapperProps}>
                    <FlagDropDown {...flagDropDownProps} />
                  </div>
                </InputGroupText>
              </InputGroupAddon>
              <Input {...inputProps} />
            </InputGroup>
          );
        }}
    onPhoneNumberChange={(v, nn) => setPhoneNumber(nn)}
  />

Screenshots:

Below are actual working implementation of above code:

Material UI:

image

Reactstrap (Bootstrap React):

image

Types of changes

Checklist:

Closes #391

andrewsantarin commented 2 years ago

UPDATE: I see it's applied. Thank you.

@andrwo Could you insert this to the description? GitHub will automatically close the issue if this gets merged.

Closes #391

mcataford commented 2 years ago

Late to the ball on this one -- I'll read through this in a couple hours to get a proper review in. 🚀