{
- this.selectedFlag = selectedFlag;
- }}
- className="flag-box"
- >
-
{
- this.selectedFlagInner = selectedFlagInner;
- }}
- className={`iti-flag ${country.iso2}`}
- />
-
-
-
{country.name}
-
-+
- {country.dialCode}
-
-
- );
- });
- };
+ dialCode={country.dialCode}
+ isoCode={country.iso2}
+ name={country.name}
+ onMouseOver={onMouseOverOrFocus}
+ onClick={() => this.props.setFlag(country.iso2)}
+ onFocus={onMouseOverOrFocus}
+ flagRef={selectedFlag => {
+ this.selectedFlag = selectedFlag
+ }}
+ innerFlagRef={selectedFlagInner => {
+ this.selectedFlagInner = selectedFlagInner
+ }}
+ countryClass={countryClass}
+ />
+ )
+ })
+ }
handleMouseOver = e => {
if (e.currentTarget.getAttribute('class').indexOf('country') > -1) {
- const selectedIndex = utils.retrieveLiIndex(e.currentTarget);
+ const selectedIndex = utils.retrieveLiIndex(e.currentTarget)
- this.props.changeHighlightCountry(true, selectedIndex);
+ this.props.changeHighlightCountry(true, selectedIndex)
}
- };
+ }
render() {
- let options = '';
- const preferredCountries = this.props.preferredCountries;
- let preferredOptions = null;
- const countries = this.props.countries;
- const className = classNames({
- 'country-list': true,
- hide: !this.props.showDropdown,
- });
- let divider = null;
-
- if (preferredCountries.length) {
- preferredOptions = this.appendListItem(preferredCountries, true);
- divider =
;
- }
+ const { preferredCountries, countries, showDropdown } = this.props
+ const className = classNames('country-list', {
+ hide: !showDropdown,
+ })
- options = this.appendListItem(countries);
+ const preferredOptions = this.appendListItem(preferredCountries, true)
+ const allOptions = this.appendListItem(countries)
+ const divider =
return (
{
- this.listElement = listElement;
+ this.listElement = listElement
}}
className={className}
>
{preferredOptions}
- {divider}
- {options}
+ {preferredCountries.length > 0 ? divider : null}
+ {allOptions}
- );
+ )
}
}
diff --git a/src/components/FlagBox.d.ts b/src/components/FlagBox.d.ts
new file mode 100644
index 000000000..1d29d5e07
--- /dev/null
+++ b/src/components/FlagBox.d.ts
@@ -0,0 +1,17 @@
+import React from 'react'
+
+export interface FlagBoxProps {
+ dialCode: string
+ isoCode: string
+ name: string
+ onMouseOver?: (event: React.MouseEvent
) => void
+ onFocus?: (event: React.FocusEvent) => void
+ onClick?: (event: React.MouseEvent) => void
+ flagRef?: (instance: HTMLDivElement | null) => void
+ innerFlagRef?: (instance: HTMLDivElement | null) => void
+ countryClass: string
+}
+
+declare const FlagBox: React.FunctionComponent
+
+export default FlagBox
diff --git a/src/components/FlagBox.js b/src/components/FlagBox.js
new file mode 100644
index 000000000..50e3aa647
--- /dev/null
+++ b/src/components/FlagBox.js
@@ -0,0 +1,50 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+const FlagBox = ({
+ dialCode,
+ isoCode,
+ name,
+ onMouseOver,
+ onFocus,
+ onClick,
+ flagRef,
+ innerFlagRef,
+ countryClass,
+}) => (
+
+
+
+ {name}
+ {`+ ${dialCode}`}
+
+)
+
+FlagBox.propTypes = {
+ dialCode: PropTypes.string.isRequired,
+ isoCode: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ onMouseOver: PropTypes.func,
+ onFocus: PropTypes.func,
+ onClick: PropTypes.func,
+ flagRef: PropTypes.func,
+ innerFlagRef: PropTypes.func,
+ countryClass: PropTypes.string.isRequired,
+}
+
+FlagBox.defaultProps = {
+ onFocus: () => {},
+ onMouseOver: () => {},
+ onClick: () => {},
+}
+
+export default FlagBox
diff --git a/src/components/FlagDropDown.d.ts b/src/components/FlagDropDown.d.ts
new file mode 100644
index 000000000..85e9553ac
--- /dev/null
+++ b/src/components/FlagDropDown.d.ts
@@ -0,0 +1,50 @@
+import React from 'react'
+
+import { CountryData } from '../types'
+
+import CountryList from './CountryList'
+
+export interface FlagDropDownProps {
+ allowDropdown?: boolean
+ dropdownContainer?: React.ElementType | string
+ separateDialCode?: boolean
+ dialCode?: string
+ countryCode?: string
+ showDropdown?: boolean
+ clickSelectedFlag?: (
+ event: React.MouseEvent,
+ ) => void
+ handleSelectedFlagKeydown?: (
+ event: React.KeyboardEvent,
+ ) => void
+ isMobile?: boolean
+ setFlag?: (iso2: string) => void
+ countries?: CountryData[]
+ inputTop?: number
+ inputOuterHeight?: number
+ preferredCountries?: CountryData[]
+ highlightedCountry?: number
+ changeHighlightCountry?: (
+ showDropdown: boolean,
+ selectedIndex: number,
+ ) => void
+ titleTip?: string
+ refCallback: (instance: HTMLDivElement | null) => void
+}
+
+export interface FlagDropDownState {}
+
+export default class FlagDropDown extends React.Component<
+ FlagDropDownProps,
+ FlagDropDownState
+> {
+ countryList?: CountryList | null
+
+ genSelectedDialCode: () => React.ReactNode
+
+ genArrow: () => React.ReactNode
+
+ genFlagClassName: () => string
+
+ genCountryList: () => React.ReactNode
+}
diff --git a/src/components/FlagDropDown.js b/src/components/FlagDropDown.js
index e38a4a644..c6a1078fa 100644
--- a/src/components/FlagDropDown.js
+++ b/src/components/FlagDropDown.js
@@ -1,8 +1,8 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import CountryList from './CountryList';
-import RootModal from './RootModal';
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import classNames from 'classnames'
+import CountryList from './CountryList'
+import RootModal from './RootModal'
export default class FlagDropDown extends Component {
static propTypes = {
@@ -24,40 +24,27 @@ export default class FlagDropDown extends Component {
changeHighlightCountry: PropTypes.func,
titleTip: PropTypes.string,
refCallback: PropTypes.func.isRequired,
- };
+ }
genSelectedDialCode = () => {
- const { separateDialCode, dialCode } = this.props;
+ const { separateDialCode, dialCode } = this.props
return separateDialCode ? (
{dialCode}
- ) : (
- ''
- );
- };
+ ) : null
+ }
genArrow = () => {
- const { allowDropdown, showDropdown } = this.props;
- const arrowClass = classNames({
- 'iti-arrow': true,
- up: showDropdown,
- });
-
- return allowDropdown ? : '';
- };
+ const { allowDropdown, showDropdown } = this.props
+ const arrowClasses = classNames('arrow', showDropdown ? 'up' : 'down')
- genFlagClassName = () => {
- const { countryCode } = this.props;
- const flagClassObj = {
- 'iti-flag': true,
- };
-
- if (countryCode) {
- flagClassObj[countryCode] = true;
- }
+ return allowDropdown ? : null
+ }
- return classNames(flagClassObj);
- };
+ genFlagClassName = () =>
+ classNames('iti-flag', {
+ [this.props.countryCode]: !!this.props.countryCode,
+ })
genCountryList = () => {
const {
@@ -72,12 +59,12 @@ export default class FlagDropDown extends Component {
preferredCountries,
highlightedCountry,
changeHighlightCountry,
- } = this.props;
+ } = this.props
return (
{
- this.countryList = countryList;
+ this.countryList = countryList
}}
dropdownContainer={dropdownContainer}
isMobile={isMobile}
@@ -90,8 +77,8 @@ export default class FlagDropDown extends Component {
highlightedCountry={highlightedCountry}
changeHighlightCountry={changeHighlightCountry}
/>
- );
- };
+ )
+ }
render() {
const {
@@ -102,7 +89,7 @@ export default class FlagDropDown extends Component {
titleTip,
dropdownContainer,
showDropdown,
- } = this.props;
+ } = this.props
return (
@@ -123,6 +110,6 @@ export default class FlagDropDown extends Component {
this.genCountryList()
)}
- );
+ )
}
}
diff --git a/src/components/IntlTelInput.d.ts b/src/components/IntlTelInput.d.ts
new file mode 100644
index 000000000..2239c59e0
--- /dev/null
+++ b/src/components/IntlTelInput.d.ts
@@ -0,0 +1,500 @@
+import React from 'react'
+
+import { CountryData } from '../types'
+
+export interface IntlTelInputProps {
+ /**
+ * Container CSS class name.
+ * @default 'intl-tel-input'
+ */
+ containerClassName?: string
+ /**
+ * Text input CSS class name.
+ * @default ''
+ */
+ inputClassName?: string
+ /**
+ * It's used as `input` field `name` attribute.
+ * @default ''
+ */
+ fieldName?: string
+ /**
+ * It's used as `input` field `id` attribute.
+ * @default ''
+ */
+ fieldId?: string
+ /**
+ * The value of the input field. Useful for making input value controlled from outside the component.
+ */
+ value?: string
+ /**
+ * The value used to initialize input. This will only work on uncontrolled component.
+ * @default ''
+ */
+ defaultValue?: string
+ /**
+ * Countries data can be configured, it defaults to data defined in `AllCountries`.
+ * @default AllCountries.getCountries()
+ */
+ countriesData?: CountryData[] | null
+ /**
+ * Whether or not to allow the dropdown. If disabled, there is no dropdown arrow, and the selected flag is not clickable.
+ * Also we display the selected flag on the right instead because it is just a marker of state.
+ * @default true
+ */
+ allowDropdown?: boolean
+ /**
+ * If there is just a dial code in the input: remove it on blur, and re-add it on focus.
+ * @default true
+ */
+ autoHideDialCode?: boolean
+ /**
+ * Add or remove input placeholder with an example number for the selected country.
+ * @default true
+ */
+ autoPlaceholder?: boolean
+ /**
+ * Change the placeholder generated by autoPlaceholder. Must return a string.
+ * @default null
+ */
+ customPlaceholder?:
+ | ((placeholder: string, selectedCountryData: CountryData) => string)
+ | null
+ /**
+ * Don't display the countries you specify. (Array)
+ * @default []
+ */
+ excludeCountries?: string[]
+ /**
+ * Format the input value during initialisation.
+ * @default true
+ */
+ formatOnInit?: boolean
+ /**
+ * Display the country dial code next to the selected flag so it's not part of the typed number.
+ * Note that this will disable nationalMode because technically we are dealing with international numbers,
+ * but with the dial code separated.
+ * @default false
+ */
+ separateDialCode?: boolean
+ /**
+ * Default country.
+ * @default ''
+ */
+ defaultCountry?: string
+ /**
+ * GeoIp lookup function.
+ * @default null
+ */
+ geoIpLookup?: (countryCode: string) => void
+ /**
+ * Don't insert international dial codes.
+ * @default true
+ */
+ nationalMode?: boolean
+ /**
+ * Number type to use for placeholders.
+ * @default 'MOBILE'
+ */
+ numberType?: string
+ /**
+ * The function which can catch the "no this default country" exception.
+ * @default null
+ */
+ noCountryDataHandler?: (countryCode: string) => void
+ /**
+ * Display only these countries.
+ * @default []
+ */
+ onlyCountries?: string[]
+ /**
+ * The countries at the top of the list. defaults to United States and United Kingdom.
+ * @default ['us', 'gb']
+ */
+ preferredCountries?: string[]
+ /**
+ * Optional validation callback function. It returns validation status, input box value and selected country data.
+ * @default null
+ */
+ onPhoneNumberChange?: (
+ isValid: boolean,
+ value: string,
+ selectedCountryData: CountryData,
+ fullNumber: string,
+ extension: string,
+ ) => void
+ /**
+ * Optional validation callback function. It returns validation status, input box value and selected country data.
+ * @default null
+ */
+ onPhoneNumberBlur?: (
+ isValid: boolean,
+ value: string,
+ selectedCountryData: CountryData,
+ fullNumber: string,
+ extension: string,
+ event: React.FocusEvent,
+ ) => void
+ /**
+ * Optional validation callback function. It returns validation status, input box value and selected country data.
+ * @default null
+ */
+ onPhoneNumberFocus?: (
+ isValid: boolean,
+ value: string,
+ selectedCountryData: CountryData,
+ fullNumber: string,
+ extension: string,
+ event: React.FocusEvent,
+ ) => void
+ /**
+ * Allow main app to do things when a country is selected.
+ * @default null
+ */
+ onSelectFlag?: (
+ currentNumber: string,
+ selectedCountryData: CountryData,
+ fullNumber: string,
+ isValid: boolean,
+ ) => void
+ /**
+ * Disable this component.
+ * @default false
+ */
+ disabled?: boolean
+ /**
+ * Static placeholder for input controller. When defined it takes priority over autoPlaceholder.
+ */
+ placeholder?: string
+ /**
+ * Enable auto focus
+ * @default false
+ */
+ autoFocus?: boolean
+ /**
+ * Set the value of the autoComplete attribute on the input.
+ * For example, set it to phone to tell the browser where to auto complete phone numbers.
+ * @default 'off'
+ */
+ autoComplete?: string
+ /**
+ * Style object for the wrapper div. Useful for setting 100% width on the wrapper, etc.
+ */
+ style?: React.CSSProperties
+ /**
+ * Render fullscreen flag dropdown when mobile useragent is detected.
+ * The dropdown element is rendered as a direct child of document.body
+ * @default true
+ */
+ useMobileFullscreenDropdown?: boolean
+ /**
+ * Pass through arbitrary props to the tel input element.
+ * @default {}
+ */
+ telInputProps?: React.InputHTMLAttributes
+ /**
+ * Format the number.
+ * @default true
+ */
+ format?: boolean
+ /**
+ * Allow main app to do things when flag icon is clicked.
+ * @default null
+ */
+ onFlagClick?: (event: React.MouseEvent) => void
+}
+
+export interface IntlTelInputState {
+ showDropdown: boolean
+ highlightedCountry: number
+ value: string
+ disabled: boolean
+ readonly: boolean
+ offsetTop: number
+ outerHeight: number
+ placeholder: string
+ title: string
+ countryCode: string
+ dialCode: string
+ cursorPosition: any
+}
+
+export default class IntlTelInput extends React.Component<
+ IntlTelInputProps,
+ IntlTelInputState
+> {
+ //#region Properties
+ wrapperClass: {
+ [key: string]: boolean
+ }
+
+ defaultCountry?: string
+
+ autoCountry: string
+
+ tempCountry: string
+
+ startedLoadingAutoCountry: boolean
+
+ dropdownContainer?: React.ElementType | ''
+
+ isOpening: boolean
+
+ isMobile: boolean
+
+ preferredCountries: CountryData[]
+
+ countries: CountryData[]
+
+ countryCodes: {
+ [key: string]: string[]
+ }
+
+ windowLoaded: boolean
+
+ query: string
+
+ selectedCountryData?: CountryData
+
+ // prop copies
+ autoHideDialCode: boolean
+
+ nationalMode: boolean
+
+ allowDropdown: boolean
+
+ // refs
+ /**
+ * `` HTML element of the `FlagDropDown` React component.
+ */
+ flagDropDown: HTMLDivElement | null
+
+ /**
+ * `
` HTML element of the `TelInput` React component.
+ */
+ tel: HTMLInputElement | null
+
+ // NOTE:
+ // The underscore.deferred package doesn't have known type definitions.
+ // The closest counterpart is jquery's Deferred object, which it claims to derive itself from.
+ // These two are equivalent if you log it in console:
+ //
+ // underscore.deferred
+ // var deferred = new _.Deferred()
+ //
+ // jquery
+ // var deferred = $.Deferred()
+ deferreds: JQuery.Deferred
[]
+
+ autoCountryDeferred: JQuery.Deferred
+
+ utilsScriptDeferred: JQuery.Deferred
+ //#endregion
+
+ //#region Methods
+ /**
+ * Updates flag when value of defaultCountry props change
+ */
+ updateFlagOnDefaultCountryChange(countryCode?: string): void
+
+ getTempCountry(countryCode?: string): CountryData['iso2'] | 'auto'
+
+ /**
+ * set the input value and update the flag
+ */
+ setNumber(number: string, preventFocus?: boolean): void
+
+ setFlagDropdownRef(ref: HTMLDivElement | null): void
+
+ setTelRef(ref: HTMLInputElement | null): void
+
+ /**
+ * select the given flag, update the placeholder and the active list item
+ *
+ * Note: called from setInitialState, updateFlagFromNumber, selectListItem, setCountry, updateFlagOnDefaultCountryChange
+ */
+ setFlag(countryCode?: string, isInit?: boolean): void
+
+ /**
+ * get the extension from the current number
+ */
+ getExtension(number?: string): string
+
+ /**
+ * format the number to the given format
+ */
+ getNumber(number?: string, format?: string): string
+
+ /**
+ * get the input val, adding the dial code if separateDialCode is enabled
+ */
+ getFullNumber(number?: string): string
+
+ /**
+ * try and extract a valid international dial code from a full telephone number
+ */
+ getDialCode(number: string): string
+
+ /**
+ * check if the given number contains an unknown area code from
+ */
+ isUnknownNanp(number?: string, dialCode?: string): boolean
+
+ /**
+ * add a country code to countryCodes
+ */
+ addCountryCode(
+ countryCodes: {
+ [key: string]: string[]
+ },
+ iso2: string,
+ dialCode: string,
+ priority?: number,
+ ): {
+ [key: string]: string[]
+ }
+
+ processAllCountries(): void
+
+ /**
+ * process the countryCodes map
+ */
+ processCountryCodes(): void
+
+ /**
+ * process preferred countries - iterate through the preferences,
+ * fetching the country data for each one
+ */
+ processPreferredCountries(): void
+
+ /**
+ * set the initial state of the input value and the selected flag
+ */
+ setInitialState(): void
+
+ initRequests(): void
+
+ loadCountryFromLocalStorage(): string
+
+ loadAutoCountry(): void
+
+ cap(number?: string): string | undefined
+
+ removeEmptyDialCode(): void
+
+ /**
+ * highlight the next/prev item in the list (and ensure it is visible)
+ */
+ handleUpDownKey(key?: number): void
+
+ /**
+ * select the currently highlighted item
+ */
+ handleEnterKey(): void
+
+ /**
+ * find the first list item whose name starts with the query string
+ */
+ searchForCountry(query: string): void
+
+ formatNumber(number?: string): string
+
+ /**
+ * update the input's value to the given val (format first if possible)
+ */
+ updateValFromNumber(
+ number?: string,
+ doFormat?: boolean,
+ doNotify?: boolean,
+ ): void
+
+ /**
+ * check if need to select a new flag based on the given number
+ */
+ updateFlagFromNumber(number?: string, isInit?: boolean): void
+
+ /**
+ * filter the given countries using the process function
+ */
+ filterCountries(
+ countryArray: string[],
+ processFunc: (iso2: string) => void,
+ ): void
+
+ /**
+ * prepare all of the country data, including onlyCountries and preferredCountries options
+ */
+ processCountryData(): void
+
+ handleOnBlur(event: React.FocusEvent): void
+
+ handleOnFocus(event: React.FocusEvent): void
+
+ bindDocumentClick(): void
+
+ unbindDocumentClick(): void
+
+ clickSelectedFlag(event: React.MouseEvent): void
+
+ /**
+ * update the input placeholder to an
+ * example number from the currently selected country
+ */
+ updatePlaceholder(props?: IntlTelInputProps): void
+
+ toggleDropdown(status?: boolean): void
+
+ /**
+ * check if an element is visible within it's container, else scroll until it is
+ */
+ scrollTo(element: Element, middle?: boolean): void
+
+ /**
+ * replace any existing dial code with the new one
+ *
+ * Note: called from _setFlag
+ */
+ updateDialCode(newDialCode?: string, hasSelectedListItem?: boolean): string
+
+ generateMarkup(): void
+
+ handleSelectedFlagKeydown(event: React.KeyboardEvent): void
+
+ /**
+ * validate the input val - assumes the global function isValidNumber (from libphonenumber)
+ */
+ isValidNumber(number?: string): boolean
+
+ formatFullNumber(number?: string): string
+
+ notifyPhoneNumberChange(number?: string): void
+
+ /**
+ * remove the dial code if separateDialCode is enabled
+ */
+ beforeSetNumber(
+ number?: string,
+ props?: IntlTelInputProps,
+ ): string | undefined
+
+ handleWindowScroll(): void
+
+ handleDocumentKeyDown(event: KeyboardEvent): void
+
+ handleDocumentClick(event: MouseEvent): void
+
+ /**
+ * Either notify phoneNumber changed if component is controlled
+ */
+ handleInputChange(event: React.FocusEvent): void
+
+ changeHighlightCountry(showDropdown: boolean, selectedIndex: number): void
+
+ loadUtils(): void
+
+ /**
+ * this is called when the geoip call returns
+ */
+ autoCountryLoaded(): void
+ //#endregion
+}
diff --git a/src/components/IntlTelInput.js b/src/components/IntlTelInput.js
index 03ec81a01..cd0f2d73d 100644
--- a/src/components/IntlTelInput.js
+++ b/src/components/IntlTelInput.js
@@ -1,19 +1,19 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import _ from 'underscore.deferred';
-import StylePropTypes from 'react-style-proptype';
-import AllCountries from './AllCountries';
-import FlagDropDown from './FlagDropDown';
-import TelInput from './TelInput';
-import utils from './utils';
-import '../styles/intlTelInput.scss';
-
-const mobileUserAgentRegexp = /Android.+Mobile|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import classNames from 'classnames'
+import _ from 'underscore.deferred'
+import StylePropTypes from 'react-style-proptype'
+import AllCountries from './AllCountries'
+import FlagDropDown from './FlagDropDown'
+import TelInput from './TelInput'
+import utils from './utils'
+import { KEYS } from './constants'
+
+const mobileUserAgentRegexp = /Android.+Mobile|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
class IntlTelInput extends Component {
static getDerivedStateFromProps(nextProps, prevState) {
- let newState = null;
+ let newState = null
if (
typeof nextProps.value !== 'undefined' &&
@@ -21,55 +21,43 @@ class IntlTelInput extends Component {
) {
newState = {
value: nextProps.value,
- };
+ }
}
if (prevState.disabled !== nextProps.disabled) {
newState = {
disabled: nextProps.disabled,
- };
+ }
}
- return newState;
+ return newState
}
constructor(props) {
- super(props);
+ super(props)
- this.wrapperClass = {};
+ this.wrapperClass = {}
- this.autoCountry = '';
- this.tempCountry = '';
- this.startedLoadingAutoCountry = false;
+ this.autoCountry = ''
+ this.tempCountry = ''
+ this.startedLoadingAutoCountry = false
- this.deferreds = [];
- this.autoCountryDeferred = new _.Deferred();
- this.utilsScriptDeferred = new _.Deferred();
+ this.deferreds = []
+ this.autoCountryDeferred = new _.Deferred()
+ this.utilsScriptDeferred = new _.Deferred()
- this.isOpening = false;
+ this.isOpening = false
this.isMobile =
typeof navigator !== 'undefined' &&
- mobileUserAgentRegexp.test(navigator.userAgent);
- this.preferredCountries = [];
- this.countries = [];
- this.countryCodes = {};
-
- this.windowLoaded = false;
-
- this.keys = {
- UP: 38,
- DOWN: 40,
- ENTER: 13,
- ESC: 27,
- PLUS: 43,
- A: 65,
- Z: 90,
- SPACE: 32,
- TAB: 9,
- };
-
- this.query = '';
- this.selectedCountryData = {};
+ mobileUserAgentRegexp.test(navigator.userAgent)
+ this.preferredCountries = []
+ this.countries = []
+ this.countryCodes = {}
+
+ this.windowLoaded = false
+
+ this.query = ''
+ this.selectedCountryData = {}
this.state = {
showDropdown: false,
@@ -84,205 +72,212 @@ class IntlTelInput extends Component {
countryCode: 'us',
dialCode: '',
cursorPosition: (props.value || props.defaultValue).length,
- };
+ }
}
componentDidMount() {
- this.autoHideDialCode = this.props.autoHideDialCode;
- this.allowDropdown = this.props.allowDropdown;
- this.nationalMode = this.props.nationalMode;
- this.dropdownContainer = '';
+ this.autoHideDialCode = this.props.autoHideDialCode
+ this.allowDropdown = this.props.allowDropdown
+ this.nationalMode = this.props.nationalMode
+ this.dropdownContainer = ''
// if in nationalMode, disable options relating to dial codes
if (this.nationalMode) {
- this.autoHideDialCode = false;
+ this.autoHideDialCode = false
}
// if separateDialCode then doesn't make sense to
// A) insert dial code into input (autoHideDialCode), and
// B) display national numbers (because we're displaying the country dial code next to them)
if (this.props.separateDialCode) {
- this.autoHideDialCode = false;
- this.nationalMode = false;
+ this.autoHideDialCode = false
+ this.nationalMode = false
// let's force this for now for simplicity - we can support this later if need be
- this.allowDropdown = true;
+ this.allowDropdown = true
}
- this.processCountryData.call(this);
- this.tempCountry = this.getTempCountry(this.props.defaultCountry);
+ this.processCountryData.call(this)
+ this.tempCountry = this.getTempCountry(this.props.defaultCountry)
if (document.readyState === 'complete') {
- this.windowLoaded = true;
+ this.windowLoaded = true
} else {
window.addEventListener('load', () => {
- this.windowLoaded = true;
- });
+ this.windowLoaded = true
+ })
}
// generate the markup
- this.generateMarkup();
+ this.generateMarkup()
// set the initial state of the input value and the selected flag
- this.setInitialState();
+ this.setInitialState()
// utils script, and auto country
- this.initRequests();
+ this.initRequests()
- this.deferreds.push(this.autoCountryDeferred.promise());
- this.deferreds.push(this.utilsScriptDeferred.promise());
+ this.deferreds.push(this.autoCountryDeferred.promise())
+ this.deferreds.push(this.utilsScriptDeferred.promise())
_.when(this.deferreds).done(() => {
- this.setInitialState();
- });
+ this.setInitialState()
+ })
- document.addEventListener('keydown', this.handleDocumentKeyDown);
+ document.addEventListener('keydown', this.handleDocumentKeyDown)
}
shouldComponentUpdate(nextProps, nextState) {
if (nextState.showDropdown) {
- document.addEventListener('keydown', this.handleDocumentKeyDown);
- this.bindDocumentClick();
+ document.addEventListener('keydown', this.handleDocumentKeyDown)
+ this.bindDocumentClick()
} else {
- document.removeEventListener('keydown', this.handleDocumentKeyDown);
- this.unbindDocumentClick();
+ document.removeEventListener('keydown', this.handleDocumentKeyDown)
+ this.unbindDocumentClick()
}
- return true;
+ return true
}
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
- this.updateFlagFromNumber(this.props.value);
+ this.updateFlagFromNumber(this.props.value)
}
if (
typeof this.props.customPlaceholder === 'function' &&
prevProps.customPlaceholder !== this.props.customPlaceholder
) {
- this.updatePlaceholder(this.props);
+ this.updatePlaceholder(this.props)
}
if (this.props.allowDropdown !== prevProps.allowDropdown) {
- this.allowDropdown = this.props.allowDropdown;
+ this.allowDropdown = this.props.allowDropdown
+ }
+
+ if (this.props.defaultCountry !== prevProps.defaultCountry) {
+ this.updateFlagOnDefaultCountryChange(this.props.defaultCountry)
}
}
componentWillUnmount() {
- document.removeEventListener('keydown', this.handleDocumentKeyDown);
- window.removeEventListener('scroll', this.handleWindowScroll);
- this.unbindDocumentClick();
+ document.removeEventListener('keydown', this.handleDocumentKeyDown)
+ window.removeEventListener('scroll', this.handleWindowScroll)
+ this.unbindDocumentClick()
+ }
+
+ // Updates flag when value of defaultCountry props change
+ updateFlagOnDefaultCountryChange = countryCode => {
+ this.setFlag(countryCode, false)
}
getTempCountry = countryCode => {
if (countryCode === 'auto') {
- return 'auto';
+ return 'auto'
}
- let countryData = utils.getCountryData(this.countries, countryCode);
+ let countryData = utils.getCountryData(this.countries, countryCode)
// check if country is available in the list
if (!countryData.iso2) {
if (this.props.preferredCountries.length > 0) {
countryData = utils.getCountryData(
this.countries,
- this.props.preferredCountries[0]
- );
+ this.props.preferredCountries[0],
+ )
} else {
- countryData = AllCountries.getCountries()[0];
+ countryData = AllCountries.getCountries()[0]
}
}
- return countryData.iso2;
- };
+ return countryData.iso2
+ }
// set the input value and update the flag
// NOTE: preventFormat arg is for public method
setNumber = (number, preventFormat) => {
// we must update the flag first, which updates this.selectedCountryData,
// which is used for formatting the number before displaying it
- this.updateFlagFromNumber(number);
- this.updateValFromNumber(number, !preventFormat);
- };
+ this.updateFlagFromNumber(number)
+ this.updateValFromNumber(number, !preventFormat)
+ }
setFlagDropdownRef = ref => {
- this.flagDropDown = ref;
- };
+ this.flagDropDown = ref
+ }
setTelRef = ref => {
- this.tel = ref;
- };
+ this.tel = ref
+ }
// select the given flag, update the placeholder and the active list item
- // Note: called from setInitialState, updateFlagFromNumber, selectListItem, setCountry
+ // Note: called from setInitialState, updateFlagFromNumber, selectListItem, setCountry, updateFlagOnDefaultCountryChange
setFlag = (countryCode, isInit) => {
const prevCountry =
this.selectedCountryData && this.selectedCountryData.iso2
? this.selectedCountryData
- : {};
+ : {}
// do this first as it will throw an error and stop if countryCode is invalid
this.selectedCountryData = countryCode
? utils.getCountryData(
- this.countries,
- countryCode,
- false,
- false,
- this.props.noCountryDataHandler
- )
- : {};
+ this.countries,
+ countryCode,
+ false,
+ false,
+ this.props.noCountryDataHandler,
+ )
+ : {}
// update the defaultCountry - we only need the iso2 from now on, so just store that
if (this.selectedCountryData.iso2) {
- this.defaultCountry = this.selectedCountryData.iso2;
+ this.defaultCountry = this.selectedCountryData.iso2
}
// update the selected country's title attribute
const title = countryCode
- ? `${this.selectedCountryData.name}: +${
- this.selectedCountryData.dialCode
- }`
- : 'Unknown';
+ ? `${this.selectedCountryData.name}: +${this.selectedCountryData.dialCode}`
+ : 'Unknown'
- let dialCode = this.state.dialCode; // eslint-disable-line react/no-access-state-in-setstate
+ let dialCode = this.state.dialCode // eslint-disable-line react/no-access-state-in-setstate
if (this.props.separateDialCode) {
dialCode = this.selectedCountryData.dialCode
? `+${this.selectedCountryData.dialCode}`
- : '';
+ : ''
if (prevCountry.dialCode) {
- delete this.wrapperClass[`iti-sdc-${prevCountry.dialCode.length + 1}`];
+ delete this.wrapperClass[`iti-sdc-${prevCountry.dialCode.length + 1}`]
}
if (dialCode) {
- this.wrapperClass[`iti-sdc-${dialCode.length}`] = true;
+ this.wrapperClass[`iti-sdc-${dialCode.length}`] = true
}
}
- let selectedIndex = 0;
+ let selectedIndex = 0
if (countryCode && countryCode !== 'auto') {
selectedIndex = utils.findIndex(
this.preferredCountries,
- country => country.iso2 === countryCode
- );
+ country => country.iso2 === countryCode,
+ )
if (selectedIndex === -1) {
selectedIndex = utils.findIndex(
this.countries,
- country => country.iso2 === countryCode
- );
- if (selectedIndex === -1) selectedIndex = 0;
- selectedIndex += this.preferredCountries.length;
+ country => country.iso2 === countryCode,
+ )
+ if (selectedIndex === -1) selectedIndex = 0
+ selectedIndex += this.preferredCountries.length
}
}
if (this.tel && this.state.showDropdown) {
- this.tel.focus();
+ this.tel.focus()
}
const newNumber = this.updateDialCode(
this.selectedCountryData.dialCode,
- !isInit
- );
+ !isInit,
+ )
this.setState(
{
@@ -295,10 +290,10 @@ class IntlTelInput extends Component {
},
() => {
// and the input's placeholder
- this.updatePlaceholder(this.props);
+ this.updatePlaceholder(this.props)
// update the active list item
- this.wrapperClass.active = false;
+ this.wrapperClass.active = false
// on change flag, trigger a custom event
// Allow Main app to do things when a country is selected
@@ -307,33 +302,33 @@ class IntlTelInput extends Component {
prevCountry.iso2 !== countryCode &&
typeof this.props.onSelectFlag === 'function'
) {
- const currentNumber = this.state.value;
+ const currentNumber = this.state.value
- const fullNumber = this.formatFullNumber(currentNumber);
- const isValid = this.isValidNumber(fullNumber);
+ const fullNumber = this.formatFullNumber(currentNumber)
+ const isValid = this.isValidNumber(fullNumber)
this.props.onSelectFlag(
currentNumber,
this.selectedCountryData,
fullNumber,
- isValid
- );
+ isValid,
+ )
}
- }
- );
- };
+ },
+ )
+ }
// get the extension from the current number
getExtension = number => {
if (window.intlTelInputUtils) {
return window.intlTelInputUtils.getExtension(
this.getFullNumber(number),
- this.selectedCountryData.iso2
- );
+ this.selectedCountryData.iso2,
+ )
}
- return '';
- };
+ return ''
+ }
// format the number to the given format
getNumber = (number, format) => {
@@ -341,73 +336,73 @@ class IntlTelInput extends Component {
return window.intlTelInputUtils.formatNumber(
this.getFullNumber(number),
this.selectedCountryData.iso2,
- format
- );
+ format,
+ )
}
- return '';
- };
+ return ''
+ }
// get the input val, adding the dial code if separateDialCode is enabled
getFullNumber = number => {
const prefix = this.props.separateDialCode
? `+${this.selectedCountryData.dialCode}`
- : '';
+ : ''
- return prefix + number;
- };
+ return prefix + number
+ }
// try and extract a valid international dial code from a full telephone number
// Note: returns the raw string inc plus character and any whitespace/dots etc
getDialCode = number => {
- let dialCode = '';
+ let dialCode = ''
// only interested in international numbers (starting with a plus)
if (number.charAt(0) === '+') {
- let numericChars = '';
+ let numericChars = ''
// iterate over chars
for (let i = 0, max = number.length; i < max; i++) {
- const c = number.charAt(i);
+ const c = number.charAt(i)
// if char is number
if (utils.isNumeric(c)) {
- numericChars += c;
+ numericChars += c
// if current numericChars make a valid dial code
if (this.countryCodes[numericChars]) {
// store the actual raw string (useful for matching later)
- dialCode = number.substr(0, i + 1);
+ dialCode = number.substr(0, i + 1)
}
// longest dial code is 4 chars
if (numericChars.length === 4) {
- break;
+ break
}
}
}
}
- return dialCode;
- };
+ return dialCode
+ }
// check if the given number contains an unknown area code from
// the North American Numbering Plan i.e. the only dialCode that
// could be extracted was +1 but the actual number's length is >=4
isUnknownNanp = (number, dialCode) => {
- return dialCode === '+1' && utils.getNumeric(number).length >= 4;
- };
+ return dialCode === '+1' && utils.getNumeric(number).length >= 4
+ }
// add a country code to countryCodes
addCountryCode = (countryCodes, iso2, dialCode, priority) => {
if (!(dialCode in countryCodes)) {
- countryCodes[dialCode] = [];
+ countryCodes[dialCode] = []
}
- const index = priority || 0;
+ const index = priority || 0
- countryCodes[dialCode][index] = iso2;
+ countryCodes[dialCode][index] = iso2
- return countryCodes;
- };
+ return countryCodes
+ }
processAllCountries = () => {
if (this.props.onlyCountries.length) {
@@ -416,28 +411,28 @@ class IntlTelInput extends Component {
this.props.onlyCountries,
inArray =>
// if country is in array
- inArray !== -1
- );
+ inArray !== -1,
+ )
} else if (this.props.excludeCountries.length) {
// process excludeCountries option
this.filterCountries(
this.props.excludeCountries,
inArray =>
// if country is not in array
- inArray === -1
- );
+ inArray === -1,
+ )
} else {
- this.countries = AllCountries.getCountries();
+ this.countries = AllCountries.getCountries()
}
- };
+ }
// process the countryCodes map
processCountryCodes = () => {
- this.countryCodes = {};
+ this.countryCodes = {}
for (let i = 0; i < this.countries.length; i++) {
- const c = this.countries[i];
+ const c = this.countries[i]
- this.addCountryCode(this.countryCodes, c.iso2, c.dialCode, c.priority);
+ this.addCountryCode(this.countryCodes, c.iso2, c.dialCode, c.priority)
// area codes
if (c.areaCodes) {
for (let j = 0; j < c.areaCodes.length; j++) {
@@ -445,49 +440,49 @@ class IntlTelInput extends Component {
this.addCountryCode(
this.countryCodes,
c.iso2,
- c.dialCode + c.areaCodes[j]
- );
+ c.dialCode + c.areaCodes[j],
+ )
}
}
}
- };
+ }
// process preferred countries - iterate through the preferences,
// fetching the country data for each one
processPreferredCountries = () => {
- this.preferredCountries = [];
+ this.preferredCountries = []
for (let i = 0, max = this.props.preferredCountries.length; i < max; i++) {
- const countryCode = this.props.preferredCountries[i].toLowerCase();
+ const countryCode = this.props.preferredCountries[i].toLowerCase()
const countryData = utils.getCountryData(
this.countries,
countryCode,
- true
- );
+ true,
+ )
if (countryData) {
- this.preferredCountries.push(countryData);
+ this.preferredCountries.push(countryData)
}
}
- };
+ }
// set the initial state of the input value and the selected flag
setInitialState = () => {
- const val = this.props.value || this.props.defaultValue || '';
+ const val = this.props.value || this.props.defaultValue || ''
// if we already have a dial code we can go ahead and set the flag, else fall back to default
if (this.getDialCode(val)) {
- this.updateFlagFromNumber(val, true);
+ this.updateFlagFromNumber(val, true)
} else if (this.tempCountry !== 'auto') {
// see if we should select a flag
if (this.tempCountry) {
- this.setFlag(this.tempCountry, true);
+ this.setFlag(this.tempCountry, true)
} else {
// no dial code and no tempCountry, so default to first in list
this.defaultCountry = this.preferredCountries.length
? this.preferredCountries[0].iso2
- : this.countries[0].iso2;
+ : this.countries[0].iso2
if (!val) {
- this.setFlag(this.defaultCountry, true);
+ this.setFlag(this.defaultCountry, true)
}
}
// if empty and no nationalMode and no autoHideDialCode then insert the default dial code
@@ -499,43 +494,49 @@ class IntlTelInput extends Component {
) {
this.setState({
value: `+${this.selectedCountryData.dialCode}`,
- });
+ })
}
}
- const doNotify = true;
+ const doNotify = true
// NOTE: if tempCountry is set to auto, that will be handled separately
// format
if (val) {
- this.updateValFromNumber(val, this.props.formatOnInit, doNotify);
+ this.updateValFromNumber(val, this.props.formatOnInit, doNotify)
}
- };
+ }
initRequests = () => {
import('libphonenumber-js-utils')
.then(() => {
- this.loadUtils();
- this.utilsScriptDeferred.resolve();
+ this.loadUtils()
+ this.utilsScriptDeferred.resolve()
})
- .catch(() => 'An error occurred while loading the component');
+ .catch(() => 'An error occurred while loading the component')
if (this.tempCountry === 'auto') {
- this.loadAutoCountry();
+ this.loadAutoCountry()
} else {
- this.autoCountryDeferred.resolve();
+ this.autoCountryDeferred.resolve()
}
- };
+ }
- loadAutoCountry = () => {
- // check for localStorage
- const lsAutoCountry =
- window.localStorage !== undefined
+ loadCountryFromLocalStorage = () => {
+ try {
+ return window.localStorage !== undefined
? window.localStorage.getItem('itiAutoCountry')
- : '';
+ : ''
+ } catch (e) {
+ return ''
+ }
+ }
+ loadAutoCountry = () => {
+ // check for localStorage
+ const lsAutoCountry = this.loadCountryFromLocalStorage()
if (lsAutoCountry) {
- this.autoCountry = lsAutoCountry;
+ this.autoCountry = lsAutoCountry
}
// 3 options:
@@ -543,16 +544,16 @@ class IntlTelInput extends Component {
// 2) not already started loading (start)
// 3) already started loading (do nothing - just wait for loading callback to fire)
if (this.autoCountry) {
- this.autoCountryLoaded();
+ this.autoCountryLoaded()
} else if (!this.startedLoadingAutoCountry) {
// don't do this twice!
- this.startedLoadingAutoCountry = true;
+ this.startedLoadingAutoCountry = true
if (typeof this.props.geoIpLookup === 'function') {
this.props.geoIpLookup(countryCode => {
- this.autoCountry = countryCode.toLowerCase();
+ this.autoCountry = countryCode.toLowerCase()
if (window.localStorage !== undefined) {
- window.localStorage.setItem('itiAutoCountry', this.autoCountry);
+ window.localStorage.setItem('itiAutoCountry', this.autoCountry)
}
// tell all instances the auto country is ready
// TODO: this should just be the current instances
@@ -561,68 +562,68 @@ class IntlTelInput extends Component {
// somewhere else).
// Using setTimeout means that the current thread of execution will finish before
// executing this, which allows the plugin to finish initialising.
- this.autoCountryLoaded();
- });
+ this.autoCountryLoaded()
+ })
}
}
- };
+ }
cap = number => {
- const max = this.tel ? this.tel.getAttribute('maxlength') : number;
+ const max = this.tel ? this.tel.getAttribute('maxlength') : number
- return max && number.length > max ? number.substr(0, max) : number;
- };
+ return max && number.length > max ? number.substr(0, max) : number
+ }
removeEmptyDialCode = () => {
- const value = this.state.value;
- const startsPlus = value.charAt(0) === '+';
+ const value = this.state.value
+ const startsPlus = value.charAt(0) === '+'
if (startsPlus) {
- const numeric = utils.getNumeric(value);
+ const numeric = utils.getNumeric(value)
// if just a plus, or if just a dial code
if (!numeric || this.selectedCountryData.dialCode === numeric) {
this.setState({
value: '',
- });
+ })
}
}
- };
+ }
// highlight the next/prev item in the list (and ensure it is visible)
handleUpDownKey = key => {
- const current = this.flagDropDown.querySelectorAll('.highlight')[0];
- const prevElement = current ? current.previousElementSibling : undefined;
- const nextElement = current ? current.nextElementSibling : undefined;
- let next = key === this.keys.UP ? prevElement : nextElement;
+ const current = this.flagDropDown.querySelectorAll('.highlight')[0]
+ const prevElement = current ? current.previousElementSibling : undefined
+ const nextElement = current ? current.nextElementSibling : undefined
+ let next = key === KEYS.UP ? prevElement : nextElement
if (next) {
// skip the divider
if (next.getAttribute('class').indexOf('divider') > -1) {
next =
- key === this.keys.UP
+ key === KEYS.UP
? next.previousElementSibling
- : next.nextElementSibling;
+ : next.nextElementSibling
}
- this.scrollTo(next);
+ this.scrollTo(next)
- const selectedIndex = utils.retrieveLiIndex(next);
+ const selectedIndex = utils.retrieveLiIndex(next)
this.setState({
showDropdown: true,
highlightedCountry: selectedIndex,
- });
+ })
}
- };
+ }
// select the currently highlighted item
handleEnterKey = () => {
- const current = this.flagDropDown.querySelectorAll('.highlight')[0];
+ const current = this.flagDropDown.querySelectorAll('.highlight')[0]
if (current) {
- const selectedIndex = utils.retrieveLiIndex(current);
- const countryCode = current.getAttribute('data-country-code');
+ const selectedIndex = utils.retrieveLiIndex(current)
+ const countryCode = current.getAttribute('data-country-code')
this.setState(
{
@@ -631,39 +632,37 @@ class IntlTelInput extends Component {
countryCode,
},
() => {
- this.setFlag(this.state.countryCode);
- this.unbindDocumentClick();
- }
- );
+ this.setFlag(this.state.countryCode)
+ this.unbindDocumentClick()
+ },
+ )
}
- };
+ }
// find the first list item whose name starts with the query string
searchForCountry = query => {
for (let i = 0, max = this.countries.length; i < max; i++) {
if (utils.startsWith(this.countries[i].name, query)) {
const listItem = this.flagDropDown.querySelector(
- `.country-list [data-country-code="${
- this.countries[i].iso2
- }"]:not(.preferred)`
- );
+ `.country-list [data-country-code="${this.countries[i].iso2}"]:not(.preferred)`,
+ )
- const selectedIndex = utils.retrieveLiIndex(listItem);
+ const selectedIndex = utils.retrieveLiIndex(listItem)
// update highlighting and scroll
this.setState({
showDropdown: true,
highlightedCountry: selectedIndex,
- });
- this.scrollTo(listItem, true);
- break;
+ })
+ this.scrollTo(listItem, true)
+ break
}
}
- };
+ }
formatNumber = number => {
if (window.intlTelInputUtils && this.selectedCountryData) {
- let format = window.intlTelInputUtils.numberFormat.INTERNATIONAL;
+ let format = window.intlTelInputUtils.numberFormat.INTERNATIONAL
if (
/* eslint-disable no-mixed-operators */
@@ -671,18 +670,18 @@ class IntlTelInput extends Component {
number.charAt(0) !== '+'
/* eslint-enable no-mixed-operators */
) {
- format = window.intlTelInputUtils.numberFormat.NATIONAL;
+ format = window.intlTelInputUtils.numberFormat.NATIONAL
}
number = window.intlTelInputUtils.formatNumber(
number,
this.selectedCountryData.iso2,
- format
- );
+ format,
+ )
}
- return number;
- };
+ return number
+ }
// update the input's value to the given val (format first if possible)
// if doNotify is true, calls notifyPhoneNumberChange with the formatted value
@@ -693,16 +692,16 @@ class IntlTelInput extends Component {
!this.props.separateDialCode &&
(this.nationalMode || number.charAt(0) !== '+')
? window.intlTelInputUtils.numberFormat.NATIONAL
- : window.intlTelInputUtils.numberFormat.INTERNATIONAL;
+ : window.intlTelInputUtils.numberFormat.INTERNATIONAL
number = window.intlTelInputUtils.formatNumber(
number,
this.selectedCountryData.iso2,
- format
- );
+ format,
+ )
}
- number = this.beforeSetNumber(number);
+ number = this.beforeSetNumber(number)
this.setState(
{
@@ -711,13 +710,13 @@ class IntlTelInput extends Component {
},
() => {
if (doNotify) {
- this.notifyPhoneNumberChange(this.state.value);
+ this.notifyPhoneNumberChange(number)
}
- this.unbindDocumentClick();
- }
- );
- };
+ this.unbindDocumentClick()
+ },
+ )
+ }
// check if need to select a new flag based on the given number
// Note: called from _setInitialState, keyup handler, setNumber
@@ -737,21 +736,21 @@ class IntlTelInput extends Component {
number.charAt(0) !== '+'
) {
if (number.charAt(0) !== '1') {
- number = `1${number}`;
+ number = `1${number}`
}
- number = `+${number}`;
+ number = `+${number}`
}
// try and extract valid dial code from input
- const dialCode = this.getDialCode(number);
- let countryCode = null;
+ const dialCode = this.getDialCode(number)
+ let countryCode = null
if (dialCode) {
// check if one of the matching countries is already selected
- const countryCodes = this.countryCodes[utils.getNumeric(dialCode)];
+ const countryCodes = this.countryCodes[utils.getNumeric(dialCode)]
const alreadySelected =
this.selectedCountryData &&
- countryCodes.indexOf(this.selectedCountryData.iso2) !== -1;
+ countryCodes.indexOf(this.selectedCountryData.iso2) !== -1
// if a matching country is not already selected
// (or this is an unknown NANP area code): choose the first in the list
@@ -760,8 +759,8 @@ class IntlTelInput extends Component {
// so we must find the first non-empty index
for (let j = 0; j < countryCodes.length; j++) {
if (countryCodes[j]) {
- countryCode = countryCodes[j];
- break;
+ countryCode = countryCodes[j]
+ break
}
}
}
@@ -769,56 +768,56 @@ class IntlTelInput extends Component {
// invalid dial code, so empty
// Note: use getNumeric here because the number has not been
// formatted yet, so could contain bad chars
- countryCode = null;
+ countryCode = null
}
if (countryCode !== null) {
- this.setFlag(countryCode, isInit);
+ this.setFlag(countryCode, isInit)
}
- };
+ }
// filter the given countries using the process function
filterCountries = (countryArray, processFunc) => {
- let i;
+ let i
// standardise case
for (i = 0; i < countryArray.length; i++) {
- countryArray[i] = countryArray[i].toLowerCase();
+ countryArray[i] = countryArray[i].toLowerCase()
}
// build instance country array
- this.countries = [];
+ this.countries = []
for (i = 0; i < AllCountries.getCountries().length; i++) {
if (
processFunc(countryArray.indexOf(AllCountries.getCountries()[i].iso2))
) {
- this.countries.push(AllCountries.getCountries()[i]);
+ this.countries.push(AllCountries.getCountries()[i])
}
}
- };
+ }
// prepare all of the country data, including onlyCountries and preferredCountries options
processCountryData = () => {
// format countries data to what is necessary for component function
// defaults to data defined in `AllCountries`
- AllCountries.initialize(this.props.countriesData);
+ AllCountries.initialize(this.props.countriesData)
// process onlyCountries or excludeCountries array if present
- this.processAllCountries.call(this);
+ this.processAllCountries.call(this)
// process the countryCodes map
- this.processCountryCodes.call(this);
+ this.processCountryCodes.call(this)
// set the preferredCountries property
- this.processPreferredCountries.call(this);
- };
+ this.processPreferredCountries.call(this)
+ }
handleOnBlur = e => {
- this.removeEmptyDialCode();
+ this.removeEmptyDialCode()
if (typeof this.props.onPhoneNumberBlur === 'function') {
- const value = this.state.value;
- const fullNumber = this.formatFullNumber(value);
- const isValid = this.isValidNumber(fullNumber);
+ const value = this.state.value
+ const fullNumber = this.formatFullNumber(value)
+ const isValid = this.isValidNumber(fullNumber)
this.props.onPhoneNumberBlur(
isValid,
@@ -826,27 +825,44 @@ class IntlTelInput extends Component {
this.selectedCountryData,
fullNumber,
this.getExtension(value),
- e
- );
+ e,
+ )
}
- };
+ }
+
+ handleOnFocus = e => {
+ if (typeof this.props.onPhoneNumberFocus === 'function') {
+ const value = this.state.value
+ const fullNumber = this.formatFullNumber(value)
+ const isValid = this.isValidNumber(fullNumber)
+
+ this.props.onPhoneNumberFocus(
+ isValid,
+ value,
+ this.selectedCountryData,
+ fullNumber,
+ this.getExtension(value),
+ e,
+ )
+ }
+ }
bindDocumentClick = () => {
- this.isOpening = true;
+ this.isOpening = true
document
.querySelector('html')
- .addEventListener('click', this.handleDocumentClick);
- };
+ .addEventListener('click', this.handleDocumentClick)
+ }
unbindDocumentClick = () => {
document
.querySelector('html')
- .removeEventListener('click', this.handleDocumentClick);
- };
+ .removeEventListener('click', this.handleDocumentClick)
+ }
clickSelectedFlag = e => {
- const { allowDropdown, onFlagClick } = this.props;
- const { showDropdown, disabled, readonly } = this.state;
+ const { allowDropdown, onFlagClick } = this.props
+ const { showDropdown, disabled, readonly } = this.state
if (!showDropdown && !disabled && !readonly && allowDropdown) {
this.setState(
@@ -856,22 +872,22 @@ class IntlTelInput extends Component {
outerHeight: utils.getOuterHeight(this.tel),
},
() => {
- const highlightItem = this.flagDropDown.querySelector('.highlight');
+ const highlightItem = this.flagDropDown.querySelector('.highlight')
if (highlightItem) {
- this.scrollTo(highlightItem, true);
+ this.scrollTo(highlightItem, true)
}
- }
- );
+ },
+ )
} else if (showDropdown) {
// need to hide dropdown when click on opened flag button
- this.toggleDropdown(false);
+ this.toggleDropdown(false)
}
// Allow main app to do things when flag icon is clicked
if (typeof onFlagClick === 'function') {
- onFlagClick(e);
+ onFlagClick(e)
}
- };
+ }
// update the input placeholder to an
// example number from the currently selected country
@@ -881,29 +897,29 @@ class IntlTelInput extends Component {
props.autoPlaceholder &&
this.selectedCountryData
) {
- const numberType = window.intlTelInputUtils.numberType[props.numberType];
+ const numberType = window.intlTelInputUtils.numberType[props.numberType]
let placeholder = this.selectedCountryData.iso2
? window.intlTelInputUtils.getExampleNumber(
- this.selectedCountryData.iso2,
- this.nationalMode,
- numberType
- )
- : '';
+ this.selectedCountryData.iso2,
+ this.nationalMode,
+ numberType,
+ )
+ : ''
- placeholder = this.beforeSetNumber(placeholder, props);
+ placeholder = this.beforeSetNumber(placeholder, props)
if (typeof props.customPlaceholder === 'function') {
placeholder = props.customPlaceholder(
placeholder,
- this.selectedCountryData
- );
+ this.selectedCountryData,
+ )
}
this.setState({
placeholder,
- });
+ })
}
- };
+ }
toggleDropdown = status => {
this.setState(
@@ -912,169 +928,169 @@ class IntlTelInput extends Component {
},
() => {
if (!this.state.showDropdown) {
- this.unbindDocumentClick();
+ this.unbindDocumentClick()
}
- }
- );
- };
+ },
+ )
+ }
// check if an element is visible within it's container, else scroll until it is
scrollTo = (element, middle) => {
try {
- const container = this.flagDropDown.querySelector('.country-list');
+ const container = this.flagDropDown.querySelector('.country-list')
const containerHeight = parseFloat(
- window.getComputedStyle(container).getPropertyValue('height')
- );
- const containerTop = utils.offset(container).top;
- const containerBottom = containerTop + containerHeight;
- const elementHeight = utils.getOuterHeight(element);
- const elementTop = utils.offset(element).top;
- const elementBottom = elementTop + elementHeight;
- const middleOffset = containerHeight / 2 - elementHeight / 2;
- let newScrollTop = elementTop - containerTop + container.scrollTop;
+ window.getComputedStyle(container).getPropertyValue('height'),
+ )
+ const containerTop = utils.offset(container).top
+ const containerBottom = containerTop + containerHeight
+ const elementHeight = utils.getOuterHeight(element)
+ const elementTop = utils.offset(element).top
+ const elementBottom = elementTop + elementHeight
+ const middleOffset = containerHeight / 2 - elementHeight / 2
+ let newScrollTop = elementTop - containerTop + container.scrollTop
if (elementTop < containerTop) {
// scroll up
if (middle) {
- newScrollTop -= middleOffset;
+ newScrollTop -= middleOffset
}
- container.scrollTop = newScrollTop;
+ container.scrollTop = newScrollTop
} else if (elementBottom > containerBottom) {
// scroll down
if (middle) {
- newScrollTop += middleOffset;
+ newScrollTop += middleOffset
}
- const heightDifference = containerHeight - elementHeight;
+ const heightDifference = containerHeight - elementHeight
- container.scrollTop = newScrollTop - heightDifference;
+ container.scrollTop = newScrollTop - heightDifference
}
} catch (err) {
// do nothing
}
- };
+ }
// replace any existing dial code with the new one
// Note: called from _setFlag
updateDialCode = (newDialCode, hasSelectedListItem) => {
- const currentNumber = this.state.value;
+ const currentNumber = this.state.value
if (!newDialCode) {
- return currentNumber;
+ return currentNumber
}
- let newNumber = currentNumber;
+ let newNumber = currentNumber
// save having to pass this every time
- newDialCode = `+${newDialCode}`;
+ newDialCode = `+${newDialCode}`
if (currentNumber.charAt(0) === '+') {
// there's a plus so we're dealing with a replacement (doesn't matter if nationalMode or not)
- const prevDialCode = this.getDialCode(currentNumber);
+ const prevDialCode = this.getDialCode(currentNumber)
if (prevDialCode) {
// current number contains a valid dial code, so replace it
- newNumber = currentNumber.replace(prevDialCode, newDialCode);
+ newNumber = currentNumber.replace(prevDialCode, newDialCode)
} else {
// current number contains an invalid dial code, so ditch it
// (no way to determine where the invalid dial code ends and the rest of the number begins)
- newNumber = newDialCode;
+ newNumber = newDialCode
}
} else if (this.nationalMode || this.props.separateDialCode) {
// don't do anything
} else if (currentNumber) {
// nationalMode is disabled
// there is an existing value with no dial code: prefix the new dial code
- newNumber = newDialCode + currentNumber;
+ newNumber = newDialCode + currentNumber
} else if (hasSelectedListItem || !this.autoHideDialCode) {
// no existing value and either they've just selected a list item, or autoHideDialCode is disabled: insert new dial code
- newNumber = newDialCode;
+ newNumber = newDialCode
}
if (newNumber !== currentNumber) {
- this.notifyPhoneNumberChange(newNumber);
+ this.notifyPhoneNumberChange(newNumber)
}
- return newNumber;
- };
+ return newNumber
+ }
generateMarkup = () => {
- this.wrapperClass['separate-dial-code'] = this.props.separateDialCode;
+ this.wrapperClass['separate-dial-code'] = this.props.separateDialCode
if (this.isMobile && this.props.useMobileFullscreenDropdown) {
- utils.addClass(document.querySelector('body'), 'iti-mobile');
+ document.querySelector('body').classList.add('iti-mobile')
// on mobile, we want a full screen dropdown, so we must append it to the body
- this.dropdownContainer = 'body';
- window.addEventListener('scroll', this.handleWindowScroll);
+ this.dropdownContainer = 'body'
+ window.addEventListener('scroll', this.handleWindowScroll)
}
- };
+ }
handleSelectedFlagKeydown = e => {
if (
!this.state.showDropdown &&
- (e.which === this.keys.UP ||
- e.which === this.keys.DOWN ||
- e.which === this.keys.SPACE ||
- e.which === this.keys.ENTER)
+ (e.which === KEYS.UP ||
+ e.which === KEYS.DOWN ||
+ e.which === KEYS.SPACE ||
+ e.which === KEYS.ENTER)
) {
// prevent form from being submitted if "ENTER" was pressed
- e.preventDefault();
+ e.preventDefault()
// prevent event from being handled again by document
- e.stopPropagation();
+ e.stopPropagation()
- this.toggleDropdown(true);
+ this.toggleDropdown(true)
}
// allow navigation from dropdown to input on TAB
- if (e.which === this.keys.TAB) {
- this.toggleDropdown(false);
+ if (e.which === KEYS.TAB) {
+ this.toggleDropdown(false)
}
- };
+ }
// validate the input val - assumes the global function isValidNumber (from libphonenumber)
isValidNumber = number => {
- const val = utils.trim(number);
+ const val = utils.trim(number)
const countryCode =
this.nationalMode || this.props.separateDialCode
? this.selectedCountryData.iso2
- : '';
+ : ''
if (window.intlTelInputUtils) {
- return window.intlTelInputUtils.isValidNumber(val, countryCode);
+ return window.intlTelInputUtils.isValidNumber(val, countryCode)
}
- return false;
- };
+ return false
+ }
formatFullNumber = number => {
return window.intlTelInputUtils
? this.getNumber(
- number,
- window.intlTelInputUtils.numberFormat.INTERNATIONAL
- )
- : number;
- };
+ number,
+ window.intlTelInputUtils.numberFormat.INTERNATIONAL,
+ )
+ : number
+ }
notifyPhoneNumberChange = newNumber => {
if (typeof this.props.onPhoneNumberChange === 'function') {
- const fullNumber = this.formatFullNumber(newNumber);
- const isValid = this.isValidNumber(fullNumber);
+ const fullNumber = this.formatFullNumber(newNumber)
+ const isValid = this.isValidNumber(fullNumber)
this.props.onPhoneNumberChange(
isValid,
newNumber,
this.selectedCountryData,
fullNumber,
- this.getExtension(newNumber)
- );
+ this.getExtension(newNumber),
+ )
}
- };
+ }
// remove the dial code if separateDialCode is enabled
beforeSetNumber = (number, props = this.props) => {
if (props.separateDialCode) {
- let dialCode = this.getDialCode(number);
+ let dialCode = this.getDialCode(number)
if (dialCode) {
// US dialCode is "+1", which is what we want
@@ -1083,7 +1099,7 @@ class IntlTelInput extends Component {
// AS dialCode is "+1 684", which is what we want
// Solution: if the country has area codes, then revert to just the dial code
if (this.selectedCountryData.areaCodes !== null) {
- dialCode = `+${this.selectedCountryData.dialCode}`;
+ dialCode = `+${this.selectedCountryData.dialCode}`
}
// a lot of numbers will have a space separating the dial code
// and the main number, and some NANP numbers will have a hyphen
@@ -1093,14 +1109,14 @@ class IntlTelInput extends Component {
const start =
number[dialCode.length] === ' ' || number[dialCode.length] === '-'
? dialCode.length + 1
- : dialCode.length;
+ : dialCode.length
- number = number.substr(start);
+ number = number.substr(start)
}
}
- return this.cap(number);
- };
+ return this.cap(number)
+ }
handleWindowScroll = () => {
this.setState(
@@ -1108,55 +1124,55 @@ class IntlTelInput extends Component {
showDropdown: false,
},
() => {
- window.removeEventListener('scroll', this.handleWindowScroll);
- }
- );
- };
+ window.removeEventListener('scroll', this.handleWindowScroll)
+ },
+ )
+ }
handleDocumentKeyDown = e => {
- let queryTimer;
+ let queryTimer
// prevent down key from scrolling the whole page,
// and enter key from submitting a form etc
- e.preventDefault();
+ e.preventDefault()
- if (e.which === this.keys.UP || e.which === this.keys.DOWN) {
+ if (e.which === KEYS.UP || e.which === KEYS.DOWN) {
// up and down to navigate
- this.handleUpDownKey(e.which);
- } else if (e.which === this.keys.ENTER) {
+ this.handleUpDownKey(e.which)
+ } else if (e.which === KEYS.ENTER) {
// enter to select
- this.handleEnterKey();
- } else if (e.which === this.keys.ESC) {
+ this.handleEnterKey()
+ } else if (e.which === KEYS.ESC) {
// esc to close
this.setState({
showDropdown: false,
- });
+ })
} else if (
- (e.which >= this.keys.A && e.which <= this.keys.Z) ||
- e.which === this.keys.SPACE
+ (e.which >= KEYS.A && e.which <= KEYS.Z) ||
+ e.which === KEYS.SPACE
) {
// upper case letters (note: keyup/keydown only return upper case letters)
// jump to countries that start with the query string
if (queryTimer) {
- clearTimeout(queryTimer);
+ clearTimeout(queryTimer)
}
if (!this.query) {
- this.query = '';
+ this.query = ''
}
- this.query += String.fromCharCode(e.which);
- this.searchForCountry(this.query);
+ this.query += String.fromCharCode(e.which)
+ this.searchForCountry(this.query)
// if the timer hits 1 second, reset the query
queryTimer = setTimeout(() => {
- this.query = '';
- }, 1000);
+ this.query = ''
+ }, 1000)
}
- };
+ }
handleDocumentClick = e => {
// Click at the outside of country list
// and outside of flag button
- const targetClass = e.target.getAttribute('class');
+ const targetClass = e.target.getAttribute('class')
if (
targetClass === null ||
@@ -1166,33 +1182,40 @@ class IntlTelInput extends Component {
targetClass.indexOf('iti-flag') === -1 &&
targetClass.indexOf('iti-arrow') === -1)
) {
- this.isOpening = false;
+ this.isOpening = false
}
if (!this.isOpening) {
- this.toggleDropdown(false);
+ this.toggleDropdown(false)
}
- this.isOpening = false;
- };
+ this.isOpening = false
+ }
// Either notify phoneNumber changed if component is controlled
// or udpate the state and notify change if component is uncontrolled
handleInputChange = e => {
- let cursorPosition = e.target.selectionStart;
- const previousValue = e.target.value;
+ let cursorPosition = e.target.selectionStart
+ // previous value is pre formatted value
+ const previousValue = e.target.value
+ // prior value is existing state value/ before update
+ const priorValue = this.state.value
const previousStringBeforeCursor =
previousValue === ''
? previousValue
- : previousValue.substring(0, cursorPosition);
- const value = this.props.format
- ? this.formatNumber(e.target.value)
- : e.target.value;
+ : previousValue.substring(0, cursorPosition)
+
+ // Don't format if user is deleting chars
+ const formattedValue =
+ previousValue.length < priorValue.length
+ ? previousValue
+ : this.formatNumber(e.target.value)
+ const value = this.props.format ? formattedValue : e.target.value
cursorPosition = utils.getCursorPositionAfterFormating(
previousStringBeforeCursor,
previousValue,
- value
- );
+ value,
+ )
if (this.props.value !== undefined) {
this.setState(
@@ -1200,10 +1223,10 @@ class IntlTelInput extends Component {
cursorPosition,
},
() => {
- this.updateFlagFromNumber(value);
- this.notifyPhoneNumberChange(value);
- }
- );
+ this.updateFlagFromNumber(value)
+ this.notifyPhoneNumberChange(value)
+ },
+ )
} else {
this.setState(
{
@@ -1211,58 +1234,52 @@ class IntlTelInput extends Component {
cursorPosition,
},
() => {
- this.updateFlagFromNumber(value);
- this.notifyPhoneNumberChange(value);
- }
- );
- }
- };
-
- handlePaste = e => {
- if (e.clipboardData) {
- this.updateFlagFromNumber(e.clipboardData.getData('Text'), false);
+ this.updateFlagFromNumber(value)
+ this.notifyPhoneNumberChange(value)
+ },
+ )
}
- };
+ }
changeHighlightCountry = (showDropdown, selectedIndex) => {
this.setState({
showDropdown,
highlightedCountry: selectedIndex,
- });
- };
+ })
+ }
loadUtils = () => {
if (window.intlTelInputUtils) {
- this.utilsScriptDeferred.resolve();
+ this.utilsScriptDeferred.resolve()
}
- };
+ }
// this is called when the geoip call returns
autoCountryLoaded = () => {
if (this.tempCountry === 'auto') {
- this.tempCountry = this.autoCountry;
- this.autoCountryDeferred.resolve();
+ this.tempCountry = this.autoCountry
+ this.autoCountryDeferred.resolve()
}
- };
+ }
render() {
- this.wrapperClass[this.props.containerClassName] = true;
- const inputClass = this.props.inputClassName;
- const wrapperStyle = Object.assign({}, this.props.style || {});
+ const inputClass = this.props.inputClassName
+ const wrapperStyle = Object.assign({}, this.props.style || {})
- this.wrapperClass['allow-dropdown'] = this.allowDropdown;
- this.wrapperClass.expanded = this.state.showDropdown;
+ this.wrapperClass['allow-dropdown'] = this.allowDropdown
+ this.wrapperClass.expanded = this.state.showDropdown
- const wrapperClass = classNames(this.wrapperClass);
+ const wrapperClass = classNames(
+ this.wrapperClass,
+ this.props.containerClassName,
+ )
const titleTip = this.selectedCountryData
- ? `${this.selectedCountryData.name}: +${
- this.selectedCountryData.dialCode
- }`
- : 'Unknown';
+ ? `${this.selectedCountryData.name}: +${this.selectedCountryData.dialCode}`
+ : 'Unknown'
const value =
- this.props.value !== undefined ? this.props.value : this.state.value;
+ this.props.value !== undefined ? this.props.value : this.state.value
return (
@@ -1290,7 +1307,7 @@ class IntlTelInput extends Component {
refCallback={this.setTelRef}
handleInputChange={this.handleInputChange}
handleOnBlur={this.handleOnBlur}
- handlePaste={this.handlePaste}
+ handleOnFocus={this.handleOnFocus}
className={inputClass}
disabled={this.state.disabled}
readonly={this.state.readonly}
@@ -1308,7 +1325,7 @@ class IntlTelInput extends Component {
cursorPosition={this.state.cursorPosition}
/>
- );
+ )
}
}
@@ -1365,6 +1382,8 @@ IntlTelInput.propTypes = {
onPhoneNumberChange: PropTypes.func,
/** Optional validation callback function. It returns validation status, input box value and selected country data. */
onPhoneNumberBlur: PropTypes.func,
+ /** Optional validation callback function. It returns validation status, input box value and selected country data. */
+ onPhoneNumberFocus: PropTypes.func,
/** Allow main app to do things when a country is selected. */
onSelectFlag: PropTypes.func,
/** Disable this component. */
@@ -1387,7 +1406,7 @@ IntlTelInput.propTypes = {
format: PropTypes.bool,
/** Allow main app to do things when flag icon is clicked. */
onFlagClick: PropTypes.func,
-};
+}
IntlTelInput.defaultProps = {
containerClassName: 'intl-tel-input',
@@ -1428,6 +1447,7 @@ IntlTelInput.defaultProps = {
preferredCountries: ['us', 'gb'],
onPhoneNumberChange: null,
onPhoneNumberBlur: null,
+ onPhoneNumberFocus: null,
onSelectFlag: null,
disabled: false,
autoFocus: false,
@@ -1439,6 +1459,6 @@ IntlTelInput.defaultProps = {
// always format the number
format: false,
onFlagClick: null,
-};
+}
-export default IntlTelInput;
+export default IntlTelInput
diff --git a/src/components/RootModal.d.ts b/src/components/RootModal.d.ts
new file mode 100644
index 000000000..549577413
--- /dev/null
+++ b/src/components/RootModal.d.ts
@@ -0,0 +1,12 @@
+import React from 'react'
+
+export interface RootModalProps {}
+
+export interface RootModalState {}
+
+export default class RootModal extends React.Component<
+ RootModalProps,
+ RootModalState
+> {
+ modalTarget: HTMLDivElement | null
+}
diff --git a/src/components/RootModal.js b/src/components/RootModal.js
index 58aa98be4..9a98d2c78 100644
--- a/src/components/RootModal.js
+++ b/src/components/RootModal.js
@@ -1,31 +1,31 @@
-import React, { Component, Fragment } from 'react';
-import PropTypes from 'prop-types';
-import ReactDOM from 'react-dom';
+import React, { Component, Fragment } from 'react'
+import PropTypes from 'prop-types'
+import ReactDOM from 'react-dom'
export default class RootModal extends Component {
static propTypes = {
children: PropTypes.node,
- };
+ }
constructor(props) {
- super(props);
+ super(props)
- this.modalTarget = document.createElement('div');
- this.modalTarget.className = 'intl-tel-input iti-container';
+ this.modalTarget = document.createElement('div')
+ this.modalTarget.className = 'intl-tel-input iti-container'
}
componentDidMount() {
- document.body.appendChild(this.modalTarget);
+ document.body.appendChild(this.modalTarget)
}
componentWillUnmount() {
- document.body.removeChild(this.modalTarget);
+ document.body.removeChild(this.modalTarget)
}
render() {
return ReactDOM.createPortal(
{this.props.children},
- this.modalTarget
- );
+ this.modalTarget,
+ )
}
}
diff --git a/src/components/TelInput.d.ts b/src/components/TelInput.d.ts
new file mode 100644
index 000000000..d7a628a8a
--- /dev/null
+++ b/src/components/TelInput.d.ts
@@ -0,0 +1,36 @@
+import React from 'react'
+
+export interface TelInputProps {
+ className?: string
+ disabled?: boolean
+ readonly?: boolean
+ fieldName?: string
+ fieldId?: string
+ value?: string
+ placeholder?: string
+ handleInputChange: (event: React.ChangeEvent) => void
+ handleOnBlur: (event: React.FocusEvent) => void
+ handleOnFocus: (event: React.FocusEvent) => void
+ autoFocus?: boolean
+ autoComplete?: string
+ inputProps?: React.HTMLProps
+ refCallback: (element: HTMLInputElement | null) => void
+ cursorPosition?: number
+}
+
+export interface TelInputState {
+ hasFocus: boolean
+}
+
+export default class TelInput extends React.Component<
+ TelInputProps,
+ TelInputState
+> {
+ tel?: HTMLInputElement | null
+
+ refHandler: (element: HTMLInputElement | null) => void
+
+ handleBlur: (event: React.FocusEvent) => void
+
+ handleFocus: (event: React.FocusEvent) => void
+}
diff --git a/src/components/TelInput.js b/src/components/TelInput.js
index 26284c2f5..63a2928ee 100644
--- a/src/components/TelInput.js
+++ b/src/components/TelInput.js
@@ -1,5 +1,5 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
export default class TelInput extends Component {
static propTypes = {
@@ -11,50 +11,48 @@ export default class TelInput extends Component {
value: PropTypes.string,
placeholder: PropTypes.string,
handleInputChange: PropTypes.func,
- handlePaste: PropTypes.func,
handleOnBlur: PropTypes.func,
+ handleOnFocus: PropTypes.func,
autoFocus: PropTypes.bool,
autoComplete: PropTypes.string,
inputProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
refCallback: PropTypes.func.isRequired,
cursorPosition: PropTypes.number,
- };
+ }
state = {
hasFocus: false,
- };
+ }
componentDidUpdate() {
if (this.state.hasFocus) {
this.tel.setSelectionRange(
this.props.cursorPosition,
- this.props.cursorPosition
- );
+ this.props.cursorPosition,
+ )
}
}
refHandler = element => {
- this.tel = element;
- this.props.refCallback(element);
- };
+ this.tel = element
+ this.props.refCallback(element)
+ }
handleBlur = e => {
- this.setState({ hasFocus: false });
+ this.setState({ hasFocus: false })
if (typeof this.props.handleOnBlur === 'function') {
- this.props.handleOnBlur(e);
+ this.props.handleOnBlur(e)
}
- };
+ }
- handleFocus = () => {
- this.setState({ hasFocus: true });
- };
+ handleFocus = e => {
+ this.setState({ hasFocus: true })
- handlePaste = e => {
- if (typeof this.props.handlePaste === 'function') {
- this.props.handlePaste(e);
+ if (typeof this.props.handleOnFocus === 'function') {
+ this.props.handleOnFocus(e)
}
- };
+ }
render() {
return (
@@ -64,18 +62,17 @@ export default class TelInput extends Component {
type="tel"
autoComplete={this.props.autoComplete}
className={this.props.className}
- disabled={this.props.disabled ? 'disabled' : false}
- readOnly={this.props.readonly ? 'readonly' : false}
+ disabled={this.props.disabled}
+ readOnly={this.props.readonly}
name={this.props.fieldName}
id={this.props.fieldId}
value={this.props.value}
placeholder={this.props.placeholder}
onChange={this.props.handleInputChange}
- onPaste={this.handlePaste}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
autoFocus={this.props.autoFocus}
/>
- );
+ )
}
}
diff --git a/src/components/__tests__/AllCountries.test.ts b/src/components/__tests__/AllCountries.test.ts
new file mode 100644
index 000000000..88bdaeb66
--- /dev/null
+++ b/src/components/__tests__/AllCountries.test.ts
@@ -0,0 +1,3 @@
+import AllCountries from '../AllCountries'
+
+console.log(AllCountries.getCountries())
diff --git a/src/components/__tests__/CountryList.test.tsx b/src/components/__tests__/CountryList.test.tsx
new file mode 100644
index 000000000..ac2bcf173
--- /dev/null
+++ b/src/components/__tests__/CountryList.test.tsx
@@ -0,0 +1,52 @@
+import React from 'react'
+
+import AllCountries from '../AllCountries'
+import CountryList from '../CountryList'
+
+const App = () => {
+ const countryListComponentRef = React.useRef(null)
+
+ const init = () => {
+ const { current: countryListComponent } = countryListComponentRef
+ if (countryListComponent == null) {
+ return
+ }
+
+ console.log('countryListComponent.listElement', countryListComponent.listElement)
+ countryListComponent.appendListItem([
+ {
+ name: '',
+ iso2: '',
+ dialCode: '',
+ priority: 0,
+ areaCodes: null,
+ }
+ ], false)
+ }
+
+ React.useEffect(() => {
+ init()
+ }, [])
+
+ const countries = AllCountries.getCountries()
+ const changeHighlightCountry = (showDropdown: boolean, selectedIndex: number) => {
+ console.log(showDropdown, selectedIndex)
+ };
+ const setFlag = (iso2: string) => {
+ console.log(iso2)
+ }
+
+ return (
+
+ )
+}
+
+React.createElement(App)
diff --git a/src/components/__tests__/FlagBox.test.tsx b/src/components/__tests__/FlagBox.test.tsx
new file mode 100644
index 000000000..e7318abef
--- /dev/null
+++ b/src/components/__tests__/FlagBox.test.tsx
@@ -0,0 +1,44 @@
+import React from 'react'
+
+import FlagBox from '../FlagBox'
+
+const App = () => {
+ const flagRef = React.useRef(null)
+ const flagRefCallback = (instance: HTMLDivElement | null) => {
+ flagRef.current = instance
+ }
+
+ const innerFlagRef = React.useRef(null)
+ const innerFlagRefCallback = (instance: HTMLDivElement | null) => {
+ innerFlagRef.current = instance
+ }
+
+ const init = () => {
+ const { current: flag } = flagRef
+ const { current: innerFlag } = flagRef
+
+ if (flag == null || innerFlag == null) {
+ return
+ }
+
+ console.log('flag.className', flag.className)
+ console.log('innerFlag.className', innerFlag.className)
+ }
+
+ React.useEffect(() => {
+ init()
+ }, [])
+
+ return (
+
+ )
+}
+
+React.createElement(App)
diff --git a/src/components/__tests__/FlagDropDown.test.js b/src/components/__tests__/FlagDropDown.test.js
index 8d5faa8b0..17f36ea4a 100644
--- a/src/components/__tests__/FlagDropDown.test.js
+++ b/src/components/__tests__/FlagDropDown.test.js
@@ -1,103 +1,125 @@
/* eslint-disable react/no-find-dom-node, no-eval */
-import React from 'react';
-import ReactDOM from 'react-dom';
-import ReactTestUtils from 'react-dom/test-utils';
-import { mount } from 'enzyme';
-import IntlTelInput from '../IntlTelInput';
-import FlagDropDown from '../FlagDropDown';
-import CountryList from '../CountryList';
-import TelInput from '../TelInput';
+import React from 'react'
+import ReactDOM from 'react-dom'
+import ReactTestUtils from 'react-dom/test-utils'
+import { mount } from 'enzyme'
+import IntlTelInput from '../IntlTelInput'
+import FlagDropDown from '../FlagDropDown'
+import CountryList from '../CountryList'
+import TelInput from '../TelInput'
// eslint-disable-next-line func-names
describe('FlagDropDown', function() {
beforeEach(() => {
- jest.resetModules();
+ jest.resetModules()
this.params = {
containerClassName: 'intl-tel-input',
inputClassName: 'form-control phoneNumber',
fieldName: 'telephone',
defaultCountry: 'tw',
- };
+ }
this.makeSubject = () => {
- return mount();
- };
- });
+ return mount()
+ }
+ })
it('should be rendered', () => {
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
- const countryListComponent = subject.find(CountryList);
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
+ const countryListComponent = subject.find(CountryList)
- expect(flagComponent.length).toBeTruthy();
- expect(countryListComponent.length).toBeTruthy();
- });
+ expect(flagComponent.length).toBeTruthy()
+ expect(countryListComponent.length).toBeTruthy()
+ })
it('should load country "jp" from localStorage', async () => {
- window.localStorage.setItem('itiAutoCountry', 'jp');
+ window.localStorage.setItem('itiAutoCountry', 'jp')
this.params = {
...this.params,
defaultCountry: 'auto',
- };
- const subject = await this.makeSubject();
+ }
+ const subject = await this.makeSubject()
subject.instance().utilsScriptDeferred.then(() => {
- expect(subject.state().countryCode).toBe('jp');
- window.localStorage.clear();
- });
- });
+ expect(subject.state().countryCode).toBe('jp')
+ window.localStorage.clear()
+ })
+ })
+
+ it('should fallback to US when localStorage is not available', async () => {
+ const mockedLocalStorage = window.localStorage
+ // This will cause calls to localStorage.getItem() to throw
+ window.localStorage = {}
+
+ this.params = {
+ ...this.params,
+ defaultCountry: 'auto',
+ }
+ const subject = await this.makeSubject()
+
+ subject.instance().utilsScriptDeferred.then(() => {
+ expect(subject.state().countryCode).toBe('us')
+ window.localStorage.clear()
+ })
+
+ window.localStorage = mockedLocalStorage
+ })
it('should has .separate-dial-code class when with separateDialCode = true', () => {
this.params = {
...this.params,
separateDialCode: true,
- };
- const subject = this.makeSubject();
+ }
+ const subject = this.makeSubject()
- expect(subject.find('.separate-dial-code').length).toBeTruthy();
- });
+ expect(subject.find('.separate-dial-code').length).toBeTruthy()
+ })
it('should has "tw" in class name', () => {
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
- expect(flagComponent.find('.iti-flag.tw').first().length).toBeTruthy();
- });
+ expect(flagComponent.find('.iti-flag.tw').first().length).toBeTruthy()
+ })
it('should not has .hide class after clicking flag component', () => {
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
expect(
- subject.find(CountryList).find('.country-list.hide').length
- ).toBeTruthy();
- flagComponent.find('.selected-flag').simulate('click');
-
- subject.update();
+ subject.find(CountryList).find('.country-list.hide').length,
+ ).toBeTruthy()
+ flagComponent
+ .find('.selected-flag')
+ .last()
+ .simulate('click')
+
+ subject.update()
expect(
- subject.find(CountryList).find('.country-list.hide').length
- ).toBeFalsy();
- });
+ subject.find(CountryList).find('.country-list.hide').length,
+ ).toBeFalsy()
+ })
it('Simulate change to Japan flag in dropdown before & after', () => {
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
- expect(subject.state().showDropdown).toBeFalsy();
- expect(flagComponent.find('.iti-flag.tw').length).toBeTruthy();
- flagComponent.simulate('click');
- const japanOption = flagComponent.find('[data-country-code="jp"]');
+ expect(subject.state().showDropdown).toBeFalsy()
+ expect(flagComponent.find('.iti-flag.tw').length).toBeTruthy()
+ flagComponent.simulate('click')
+ const japanOption = flagComponent.find('[data-country-code="jp"]')
- japanOption.simulate('click');
- expect(flagComponent.find('.iti-flag.jp').length).toBeTruthy();
- expect(subject.state().showDropdown).toBeFalsy();
- });
+ japanOption.simulate('click')
+ expect(flagComponent.find('.iti-flag.jp').length).toBeTruthy()
+ expect(subject.state().showDropdown).toBeFalsy()
+ })
it('Set onlyCountries', () => {
- this.params.onlyCountries = ['tw', 'us', 'kr'];
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
+ this.params.onlyCountries = ['tw', 'us', 'kr']
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
const result = [
{
@@ -121,35 +143,35 @@ describe('FlagDropDown', function() {
priority: 0,
areaCodes: null,
},
- ];
+ ]
- expect(flagComponent.props().countries).toEqual(result);
- });
+ expect(flagComponent.props().countries).toEqual(result)
+ })
it('Set excludeCountries', () => {
- this.params.excludeCountries = ['us', 'kr'];
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
+ this.params.excludeCountries = ['us', 'kr']
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
- expect(flagComponent.props().countries.length).toBe(241);
- });
+ expect(flagComponent.props().countries.length).toBe(241)
+ })
it('Set defaultCountry as "auto"', async () => {
const lookup = callback => {
- callback('jp');
- };
+ callback('jp')
+ }
this.params = {
...this.params,
defaultCountry: 'auto',
geoIpLookup: lookup,
- };
- const subject = await this.makeSubject();
+ }
+ const subject = await this.makeSubject()
subject.instance().utilsScriptDeferred.then(() => {
- expect(subject.state().countryCode).toBe('jp');
- });
- });
+ expect(subject.state().countryCode).toBe('jp')
+ })
+ })
describe('with original ReactTestUtils', () => {
it('Mouse over on country', () => {
@@ -158,38 +180,38 @@ describe('FlagDropDown', function() {
css={['intl-tel-input', 'form-control phoneNumber']}
fieldName="telephone"
defaultCountry="tw"
- />
- );
+ />,
+ )
const flagComponent = ReactTestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
- 'selected-flag'
- );
+ 'selected-flag',
+ )
const dropDownComponent = ReactTestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
- 'country-list'
- );
+ 'country-list',
+ )
- ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(flagComponent));
+ ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(flagComponent))
const options = ReactDOM.findDOMNode(dropDownComponent).querySelectorAll(
- '.country:not([class="preferred"])'
- );
+ '.country:not([class="preferred"])',
+ )
const koreaOption = ReactDOM.findDOMNode(dropDownComponent).querySelector(
- '[data-country-code="kr"]'
- );
+ '[data-country-code="kr"]',
+ )
- let index = -1;
+ let index = -1
for (let i = 0, max = options.length; i < max; ++i) {
if (options[i] === koreaOption) {
- index = i;
+ index = i
}
}
- ReactTestUtils.Simulate.mouseOver(koreaOption);
- expect(renderedComponent.state.highlightedCountry).toBe(index);
- });
+ ReactTestUtils.Simulate.mouseOver(koreaOption)
+ expect(renderedComponent.state.highlightedCountry).toBe(index)
+ })
it('Simulate change to flag in dropdown by up and down key', () => {
const renderedComponent = ReactTestUtils.renderIntoDocument(
@@ -197,37 +219,38 @@ describe('FlagDropDown', function() {
css={['intl-tel-input', 'form-control phoneNumber']}
fieldName="telephone"
defaultCountry="tw"
- />
- );
+ />,
+ )
const flagComponent = ReactTestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
- 'selected-flag'
- );
+ 'selected-flag',
+ )
expect(
- ReactDOM.findDOMNode(flagComponent).querySelector('.iti-flag').className
- ).toBe('iti-flag tw');
+ ReactDOM.findDOMNode(flagComponent).querySelector('.iti-flag')
+ .className,
+ ).toBe('iti-flag tw')
ReactTestUtils.Simulate.keyDown(ReactDOM.findDOMNode(flagComponent), {
key: 'Enter',
keyCode: 13,
which: 13,
- });
- expect(renderedComponent.state.showDropdown).toBeTruthy();
+ })
+ expect(renderedComponent.state.showDropdown).toBeTruthy()
ReactTestUtils.Simulate.keyDown(ReactDOM.findDOMNode(flagComponent), {
key: 'Tab',
keyCode: 9,
which: 9,
- });
- expect(renderedComponent.state.showDropdown).toBeFalsy();
+ })
+ expect(renderedComponent.state.showDropdown).toBeFalsy()
ReactTestUtils.Simulate.keyDown(ReactDOM.findDOMNode(flagComponent), {
key: 'Enter',
keyCode: 13,
which: 13,
- });
+ })
const pressUpEvent = new window.KeyboardEvent('keydown', {
bubbles: true,
@@ -236,10 +259,10 @@ describe('FlagDropDown', function() {
keyCode: 38,
key: 'Up',
which: 38,
- });
+ })
- document.dispatchEvent(pressUpEvent);
- expect(renderedComponent.state.highlightedCountry).toBe(212);
+ document.dispatchEvent(pressUpEvent)
+ expect(renderedComponent.state.highlightedCountry).toBe(212)
const pressEnterEvent = new window.KeyboardEvent('keydown', {
bubbles: true,
@@ -248,15 +271,15 @@ describe('FlagDropDown', function() {
keyCode: 13,
key: 'Enter',
which: 13,
- });
+ })
- document.dispatchEvent(pressEnterEvent);
- expect(renderedComponent.state.showDropdown).toBeFalsy();
+ document.dispatchEvent(pressEnterEvent)
+ expect(renderedComponent.state.showDropdown).toBeFalsy()
expect(
ReactDOM.findDOMNode(flagComponent).querySelector('.iti-flag')
- .className === 'iti-flag sy'
- );
- });
+ .className === 'iti-flag sy',
+ )
+ })
it('Simulate close the dropdown menu by ESC key', () => {
const renderedComponent = ReactTestUtils.renderIntoDocument(
@@ -264,20 +287,20 @@ describe('FlagDropDown', function() {
css={['intl-tel-input', 'form-control phoneNumber']}
fieldName="telephone"
defaultCountry="tw"
- />
- );
+ />,
+ )
const flagComponent = ReactTestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
- 'selected-flag'
- );
+ 'selected-flag',
+ )
ReactTestUtils.Simulate.keyDown(ReactDOM.findDOMNode(flagComponent), {
key: 'Enter',
keyCode: 13,
which: 13,
- });
- expect(renderedComponent.state.showDropdown).toBeTruthy();
+ })
+ expect(renderedComponent.state.showDropdown).toBeTruthy()
const pressEscEvent = new window.KeyboardEvent('keydown', {
bubbles: true,
@@ -286,11 +309,11 @@ describe('FlagDropDown', function() {
keyCode: 27,
key: 'Esc',
which: 27,
- });
+ })
- document.dispatchEvent(pressEscEvent);
- expect(renderedComponent.state.showDropdown).toBeFalsy();
- });
+ document.dispatchEvent(pressEscEvent)
+ expect(renderedComponent.state.showDropdown).toBeFalsy()
+ })
it('Simulate close the dropdown menu by clicking on document', () => {
const renderedComponent = ReactTestUtils.renderIntoDocument(
@@ -298,30 +321,30 @@ describe('FlagDropDown', function() {
css={['intl-tel-input', 'form-control phoneNumber']}
fieldName="telephone"
defaultCountry="tw"
- />
- );
+ />,
+ )
const flagComponent = ReactTestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
- 'selected-flag'
- );
+ 'selected-flag',
+ )
ReactTestUtils.Simulate.keyDown(ReactDOM.findDOMNode(flagComponent), {
key: 'Enter',
keyCode: 13,
which: 13,
- });
- expect(renderedComponent.state.showDropdown).toBeTruthy();
+ })
+ expect(renderedComponent.state.showDropdown).toBeTruthy()
const clickEvent = new window.MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
- });
+ })
- document.querySelector('html').dispatchEvent(clickEvent);
- expect(renderedComponent.state.showDropdown).toBeFalsy();
- });
+ document.querySelector('html').dispatchEvent(clickEvent)
+ expect(renderedComponent.state.showDropdown).toBeFalsy()
+ })
it('componentWillUnmount', () => {
const renderedComponent = ReactTestUtils.renderIntoDocument(
@@ -329,32 +352,32 @@ describe('FlagDropDown', function() {
css={['intl-tel-input', 'form-control phoneNumber']}
fieldName="telephone"
defaultCountry="tw"
- />
- );
+ />,
+ )
const flagComponent = ReactTestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
- 'selected-flag'
- );
+ 'selected-flag',
+ )
ReactTestUtils.Simulate.keyDown(ReactDOM.findDOMNode(flagComponent), {
key: 'Enter',
keyCode: 13,
which: 13,
- });
- expect(renderedComponent.state.showDropdown).toBeTruthy();
+ })
+ expect(renderedComponent.state.showDropdown).toBeTruthy()
- renderedComponent.componentWillUnmount();
+ renderedComponent.componentWillUnmount()
const clickEvent = new window.MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
- });
+ })
- document.querySelector('html').dispatchEvent(clickEvent);
- expect(renderedComponent.state.showDropdown).toBeTruthy();
- });
+ document.querySelector('html').dispatchEvent(clickEvent)
+ expect(renderedComponent.state.showDropdown).toBeTruthy()
+ })
it('Simulate search country name in dropdown menu', () => {
const renderedComponent = ReactTestUtils.renderIntoDocument(
@@ -362,20 +385,20 @@ describe('FlagDropDown', function() {
css={['intl-tel-input', 'form-control phoneNumber']}
fieldName="telephone"
defaultCountry="tw"
- />
- );
+ />,
+ )
const flagComponent = ReactTestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
- 'selected-flag'
- );
+ 'selected-flag',
+ )
ReactTestUtils.Simulate.keyDown(ReactDOM.findDOMNode(flagComponent), {
key: 'Enter',
keyCode: 13,
which: 13,
- });
- expect(renderedComponent.state.showDropdown).toBe(true);
+ })
+ expect(renderedComponent.state.showDropdown).toBe(true)
const pressJEvent = new window.KeyboardEvent('keydown', {
bubbles: true,
@@ -384,7 +407,7 @@ describe('FlagDropDown', function() {
keyCode: 74,
key: 'J',
which: 74,
- });
+ })
const pressAEvent = new window.KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
@@ -392,7 +415,7 @@ describe('FlagDropDown', function() {
keyCode: 65,
key: 'A',
which: 65,
- });
+ })
const pressPEvent = new window.KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
@@ -400,11 +423,11 @@ describe('FlagDropDown', function() {
keyCode: 80,
key: 'P',
which: 80,
- });
+ })
- document.dispatchEvent(pressJEvent);
- document.dispatchEvent(pressAEvent);
- document.dispatchEvent(pressPEvent);
+ document.dispatchEvent(pressJEvent)
+ document.dispatchEvent(pressAEvent)
+ document.dispatchEvent(pressPEvent)
const pressEnterEvent = new window.KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
@@ -412,55 +435,55 @@ describe('FlagDropDown', function() {
keyCode: 13,
key: 'Enter',
which: 13,
- });
+ })
- document.dispatchEvent(pressEnterEvent);
+ document.dispatchEvent(pressEnterEvent)
- expect(renderedComponent.state.showDropdown).toBeFalsy();
- expect(renderedComponent.state.highlightedCountry).toBe(108);
- expect(renderedComponent.state.countryCode).toBe('jp');
- });
- });
+ expect(renderedComponent.state.showDropdown).toBeFalsy()
+ expect(renderedComponent.state.highlightedCountry).toBe(108)
+ expect(renderedComponent.state.countryCode).toBe('jp')
+ })
+ })
it('customPlaceholder', () => {
- let expected = '';
+ let expected = ''
const customPlaceholder = (placeholder, countryData) => {
- expected = `${placeholder},${countryData.iso2}`;
- };
+ expected = `${placeholder},${countryData.iso2}`
+ }
- this.params.customPlaceholder = customPlaceholder;
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
- const countryListComponent = subject.find(CountryList);
+ this.params.customPlaceholder = customPlaceholder
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
+ const countryListComponent = subject.find(CountryList)
- expect(expected).toBe('0912 345 678,tw');
- flagComponent.simulate('click');
- const japanOption = countryListComponent.find('[data-country-code="jp"]');
+ expect(expected).toBe('0912 345 678,tw')
+ flagComponent.simulate('click')
+ const japanOption = countryListComponent.find('[data-country-code="jp"]')
- japanOption.simulate('click');
- expect(expected).toBe('090-1234-5678,jp');
- });
+ japanOption.simulate('click')
+ expect(expected).toBe('090-1234-5678,jp')
+ })
it('onSelectFlag', () => {
- let expected = '';
+ let expected = ''
const onSelectFlag = (currentNumber, countryData, fullNumber, isValid) => {
expected = Object.assign(
{},
- { currentNumber, fullNumber, isValid, ...countryData }
- );
- };
+ { currentNumber, fullNumber, isValid, ...countryData },
+ )
+ }
- this.params.onSelectFlag = onSelectFlag;
- const subject = this.makeSubject();
- const flagComponent = subject.find(FlagDropDown);
- const inputComponent = subject.find(TelInput);
- const countryListComponent = subject.find(CountryList);
+ this.params.onSelectFlag = onSelectFlag
+ const subject = this.makeSubject()
+ const flagComponent = subject.find(FlagDropDown)
+ const inputComponent = subject.find(TelInput)
+ const countryListComponent = subject.find(CountryList)
- inputComponent.simulate('change', { target: { value: '+8109012345678' } });
- flagComponent.simulate('click');
- const japanOption = countryListComponent.find('[data-country-code="jp"]');
+ inputComponent.simulate('change', { target: { value: '+8109012345678' } })
+ flagComponent.simulate('click')
+ const japanOption = countryListComponent.find('[data-country-code="jp"]')
- japanOption.simulate('click');
+ japanOption.simulate('click')
expect(expected).toEqual({
currentNumber: '+8109012345678',
@@ -471,29 +494,29 @@ describe('FlagDropDown', function() {
dialCode: '81',
priority: 0,
areaCodes: null,
- });
- });
+ })
+ })
it('should output formatted number with formatNumber function', () => {
- this.params.format = true;
- this.params.nationalMode = true;
- const subject = this.makeSubject();
+ this.params.format = true
+ this.params.nationalMode = true
+ const subject = this.makeSubject()
expect(subject.instance().formatNumber('+886 912 345 678')).toBe(
- '0912 345 678'
- );
- });
+ '0912 345 678',
+ )
+ })
it('should highlight country from preferred list', async () => {
- const { defaultCountry } = this.params;
+ const { defaultCountry } = this.params
this.params = {
...this.params,
preferredCountries: ['us', 'gb', defaultCountry],
- };
- const subject = await this.makeSubject();
+ }
+ const subject = await this.makeSubject()
- expect(defaultCountry).toBeTruthy();
- expect(subject.state().highlightedCountry).toBe(2);
- });
-});
+ expect(defaultCountry).toBeTruthy()
+ expect(subject.state().highlightedCountry).toBe(2)
+ })
+})
diff --git a/src/components/__tests__/FlagDropDown.test.tsx b/src/components/__tests__/FlagDropDown.test.tsx
new file mode 100644
index 000000000..b6a9b8114
--- /dev/null
+++ b/src/components/__tests__/FlagDropDown.test.tsx
@@ -0,0 +1,57 @@
+import React from 'react'
+
+import FlagDropDown from '../FlagDropDown'
+
+const App = () => {
+ const flagDropDownComponentRef = React.useRef(null)
+ const flagDropDownElementRef = React.useRef(null)
+ const refCallback = (instance: HTMLDivElement | null) => {
+ flagDropDownElementRef.current = instance
+ }
+
+ const init = () => {
+ const { current: flagDropDownComponent } = flagDropDownComponentRef
+ if (flagDropDownComponent == null) {
+ return
+ }
+
+ const { current: flagDropDownElement } = flagDropDownElementRef
+ if (flagDropDownElement == null) {
+ return
+ }
+
+ console.log('flagDropDownElement.className', flagDropDownElement.className)
+ console.log(
+ 'flagDropDownComponent.countryList',
+ flagDropDownComponent.countryList,
+ )
+
+ console.log(
+ 'flagDropDownComponent.genArrow()',
+ flagDropDownComponent.genArrow(),
+ )
+ console.log(
+ 'flagDropDownComponent.genCountryList()',
+ flagDropDownComponent.genCountryList(),
+ )
+ console.log(
+ 'flagDropDownComponent.genSelectedDialCode()',
+ flagDropDownComponent.genSelectedDialCode(),
+ )
+ }
+
+ React.useEffect(() => {
+ init()
+ }, [])
+
+ return (
+
+ )
+}
+
+React.createElement(App)
diff --git a/src/components/__tests__/IntlTelInput.test.js b/src/components/__tests__/IntlTelInput.test.js
new file mode 100644
index 000000000..38d84b87a
--- /dev/null
+++ b/src/components/__tests__/IntlTelInput.test.js
@@ -0,0 +1,20 @@
+import React from 'react'
+import { shallow } from 'enzyme'
+
+import IntlTelInput from '../IntlTelInput'
+
+describe('Style customization', () => {
+ it('correctly applies user-supplied classes on outer container', () => {
+ const component = shallow()
+ const mockClass = 'mock-class-1'
+ component.setProps({ containerClassName: mockClass })
+ expect(component.props().className).toMatchInlineSnapshot(
+ `"allow-dropdown mock-class-1"`,
+ )
+ const otherMockClass = 'mock-class-2'
+ component.setProps({ containerClassName: otherMockClass })
+ expect(component.props().className).toMatchInlineSnapshot(
+ `"allow-dropdown mock-class-2"`,
+ )
+ })
+})
diff --git a/src/components/__tests__/IntlTelInput.test.tsx b/src/components/__tests__/IntlTelInput.test.tsx
new file mode 100644
index 000000000..c9a79deaa
--- /dev/null
+++ b/src/components/__tests__/IntlTelInput.test.tsx
@@ -0,0 +1,109 @@
+import React from 'react'
+
+import { CountryData } from '../../types'
+import IntlTelInput from '../IntlTelInput'
+
+interface AppProps {}
+interface AppState {
+ value: string
+ fullNumber: string
+ iso2: string
+}
+
+class App extends React.Component {
+ intlTelInput: React.RefObject = React.createRef()
+
+ state: AppState = {
+ value: '',
+ fullNumber: '',
+ iso2: '',
+ }
+
+ handlePhoneNumberChange = (
+ isValid: boolean,
+ value: string,
+ seletedCountryData: CountryData,
+ fullNumber: string,
+ extension: string,
+ ) => {
+ console.log('Details:', {
+ isValid,
+ value,
+ fullNumber,
+ extension,
+ })
+ const { iso2 = '' } = seletedCountryData
+
+ this.setState({
+ value,
+ fullNumber,
+ iso2,
+ })
+ }
+
+ handlePhoneNumberBlur = (
+ isValid: boolean,
+ value: string,
+ seletedCountryData: CountryData,
+ fullNumber: string,
+ extension: string,
+ event: React.FocusEvent,
+ ) => {
+ console.log('Blur event', event)
+ console.log('Native event type:', event.type)
+ console.log('Details:', {
+ isValid,
+ value,
+ seletedCountryData,
+ fullNumber,
+ extension,
+ event,
+ })
+ }
+
+ handlePhoneNumberFocus = (
+ isValid: boolean,
+ value: string,
+ seletedCountryData: CountryData,
+ fullNumber: string,
+ extension: string,
+ event: React.FocusEvent,
+ ) => {
+ console.log('Focus event')
+ console.log('Native event type:', event.type)
+ console.log('Details:', {
+ isValid,
+ value,
+ seletedCountryData,
+ fullNumber,
+ extension,
+ event,
+ })
+ }
+
+ render() {
+ const { value, fullNumber, iso2 } = this.state
+
+ return (
+
+