@transferwise/components 0.0.0-experimental-778ee44 → 0.0.0-experimental-ae2a4e7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.esm.js +226 -327
- package/build/index.esm.js.map +1 -1
- package/build/index.js +227 -328
- package/build/index.js.map +1 -1
- package/build/types/common/locale/index.d.ts +26 -43
- package/build/types/common/locale/index.d.ts.map +1 -1
- package/build/types/common/textFormat/formatWithPattern/formatWithPattern.d.ts +1 -1
- package/build/types/common/textFormat/formatWithPattern/formatWithPattern.d.ts.map +1 -1
- package/build/types/common/textFormat/getCursorPositionAfterKeystroke/getCursorPositionAfterKeystroke.d.ts +2 -3
- package/build/types/common/textFormat/getCursorPositionAfterKeystroke/getCursorPositionAfterKeystroke.d.ts.map +1 -1
- package/build/types/common/textFormat/getSymbolsInPatternWithPosition/getSymbolsInPatternWithPosition.d.ts +1 -5
- package/build/types/common/textFormat/getSymbolsInPatternWithPosition/getSymbolsInPatternWithPosition.d.ts.map +1 -1
- package/build/types/common/textFormat/unformatWithPattern/unformatWithPattern.d.ts +1 -1
- package/build/types/common/textFormat/unformatWithPattern/unformatWithPattern.d.ts.map +1 -1
- package/build/types/index.d.ts +0 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +11 -5
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/index.d.ts +1 -2
- package/build/types/inputWithDisplayFormat/index.d.ts.map +1 -1
- package/build/types/inputs/Input.d.ts +6 -8
- package/build/types/inputs/Input.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts +30 -27
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/phoneNumberInput/data/countries.d.ts +5 -10
- package/build/types/phoneNumberInput/data/countries.d.ts.map +1 -1
- package/build/types/phoneNumberInput/index.d.ts +1 -1
- package/build/types/phoneNumberInput/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/cleanNumber/cleanNumber.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/cleanNumber/cleanNumber.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/cleanNumber/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/cleanNumber/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/excludeCountries/excludeCountries.d.ts +8 -1
- package/build/types/phoneNumberInput/utils/excludeCountries/excludeCountries.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/excludeCountries/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/excludeCountries/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/explodeNumberModel/index.d.ts +8 -4
- package/build/types/phoneNumberInput/utils/explodeNumberModel/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/findCountryByCode/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/findCountryByCode/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/findCountryByPrefix/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/findCountryByPrefix/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.d.ts +2 -1
- package/build/types/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/groupCountriesByPrefix/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/groupCountriesByPrefix/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/index.d.ts +11 -13
- package/build/types/phoneNumberInput/utils/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/isStringNumeric/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/isStringNumeric/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/isStringNumeric/isStringNumeric.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/isStringNumeric/isStringNumeric.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/isValidPhoneNumber/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/isValidPhoneNumber/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.d.ts +6 -1
- package/build/types/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/longestMatchingPrefix/index.d.ts +2 -1
- package/build/types/phoneNumberInput/utils/longestMatchingPrefix/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/setDefaultPrefix/index.d.ts +7 -1
- package/build/types/phoneNumberInput/utils/setDefaultPrefix/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/sortArrayByProperty/index.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/sortArrayByProperty/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.d.ts +1 -1
- package/build/types/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.d.ts.map +1 -1
- package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts +11 -5
- package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts.map +1 -1
- package/build/types/textareaWithDisplayFormat/index.d.ts +1 -2
- package/build/types/textareaWithDisplayFormat/index.d.ts.map +1 -1
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts +82 -55
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
- package/build/types/withDisplayFormat/index.d.ts +1 -2
- package/build/types/withDisplayFormat/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/locale/{index.spec.js → index.spec.ts} +0 -4
- package/src/common/locale/index.ts +96 -0
- package/src/common/textFormat/formatWithPattern/{formatWithPattern.ts → formatWithPattern.js} +4 -8
- package/src/common/textFormat/getCursorPositionAfterKeystroke/{getCursorPositionAfterKeystroke.ts → getCursorPositionAfterKeystroke.js} +8 -9
- package/src/common/textFormat/getSymbolsInPatternWithPosition/{getSymbolsInPatternWithPosition.ts → getSymbolsInPatternWithPosition.js} +2 -7
- package/src/common/textFormat/unformatWithPattern/{unformatWithPattern.ts → unformatWithPattern.js} +2 -3
- package/src/index.ts +0 -2
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.js +14 -0
- package/src/inputWithDisplayFormat/index.js +1 -0
- package/src/inputs/Input.tsx +11 -6
- package/src/phoneNumberInput/PhoneNumberInput.spec.js +18 -22
- package/src/phoneNumberInput/PhoneNumberInput.tsx +201 -0
- package/src/phoneNumberInput/data/{countries.js → countries.ts} +9 -1
- package/src/phoneNumberInput/utils/cleanNumber/cleanNumber.ts +3 -0
- package/src/phoneNumberInput/utils/excludeCountries/{excludeCountries.spec.js → excludeCountries.spec.ts} +1 -1
- package/src/phoneNumberInput/utils/excludeCountries/{excludeCountries.js → excludeCountries.ts} +6 -5
- package/src/phoneNumberInput/utils/explodeNumberModel/{explodeNumberModel.spec.js → explodeNumberModel.spec.ts} +1 -1
- package/src/phoneNumberInput/utils/explodeNumberModel/index.ts +25 -0
- package/src/phoneNumberInput/utils/findCountryByCode/{findCountryByCode.spec.js → findCountryByCode.spec.ts} +0 -1
- package/src/phoneNumberInput/utils/findCountryByCode/index.ts +12 -0
- package/src/phoneNumberInput/utils/findCountryByPrefix/index.ts +12 -0
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.spec.ts +102 -0
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.ts +12 -0
- package/src/phoneNumberInput/utils/{index.js → index.ts} +0 -2
- package/src/phoneNumberInput/utils/isStringNumeric/{isStringNumeric.spec.js → isStringNumeric.spec.ts} +0 -1
- package/src/phoneNumberInput/utils/isStringNumeric/isStringNumeric.ts +1 -0
- package/src/phoneNumberInput/utils/isValidPhoneNumber/{isValidPhoneNumber.spec.js → isValidPhoneNumber.spec.ts} +1 -1
- package/src/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.ts +7 -0
- package/src/phoneNumberInput/utils/longestMatchingPrefix/index.ts +4 -0
- package/src/phoneNumberInput/utils/setDefaultPrefix/index.ts +20 -0
- package/src/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.ts +6 -0
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.js +14 -0
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.spec.js +1 -3
- package/src/textareaWithDisplayFormat/index.js +1 -0
- package/src/withDisplayFormat/{WithDisplayFormat.tsx → WithDisplayFormat.js} +104 -111
- package/src/withDisplayFormat/index.js +1 -0
- package/build/types/phoneNumberInput/utils/filterOptionsForQuery/index.d.ts +0 -2
- package/build/types/phoneNumberInput/utils/filterOptionsForQuery/index.d.ts.map +0 -1
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/index.d.ts +0 -2
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/index.d.ts.map +0 -1
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.d.ts +0 -3
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.d.ts.map +0 -1
- package/src/common/locale/index.js +0 -139
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +0 -11
- package/src/inputWithDisplayFormat/index.ts +0 -2
- package/src/phoneNumberInput/PhoneNumberInput.js +0 -210
- package/src/phoneNumberInput/data/countries.spec.js +0 -12
- package/src/phoneNumberInput/utils/cleanNumber/cleanNumber.js +0 -4
- package/src/phoneNumberInput/utils/explodeNumberModel/index.js +0 -27
- package/src/phoneNumberInput/utils/filterOptionsForQuery/filterOptionsForQuery.spec.js +0 -36
- package/src/phoneNumberInput/utils/filterOptionsForQuery/index.js +0 -11
- package/src/phoneNumberInput/utils/findCountryByCode/index.js +0 -10
- package/src/phoneNumberInput/utils/findCountryByPrefix/index.js +0 -11
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.js +0 -26
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.spec.js +0 -67
- package/src/phoneNumberInput/utils/isOptionAndFitsQuery/index.js +0 -1
- package/src/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.js +0 -25
- package/src/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.spec.js +0 -66
- package/src/phoneNumberInput/utils/isStringNumeric/isStringNumeric.js +0 -1
- package/src/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.js +0 -10
- package/src/phoneNumberInput/utils/longestMatchingPrefix/index.js +0 -2
- package/src/phoneNumberInput/utils/setDefaultPrefix/index.js +0 -25
- package/src/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.js +0 -3
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +0 -32
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +0 -11
- package/src/textareaWithDisplayFormat/index.ts +0 -2
- package/src/withDisplayFormat/index.ts +0 -2
- /package/src/phoneNumberInput/{PhoneNumberInput.story.js → PhoneNumberInput.story.tsx} +0 -0
- /package/src/phoneNumberInput/{index.js → index.ts} +0 -0
- /package/src/phoneNumberInput/utils/cleanNumber/{cleanNumber.spec.js → cleanNumber.spec.ts} +0 -0
- /package/src/phoneNumberInput/utils/cleanNumber/{index.js → index.ts} +0 -0
- /package/src/phoneNumberInput/utils/excludeCountries/{index.js → index.ts} +0 -0
- /package/src/phoneNumberInput/utils/findCountryByPrefix/{findCountryByPrefix.spec.js → findCountryByPrefix.spec.ts} +0 -0
- /package/src/phoneNumberInput/utils/groupCountriesByPrefix/{index.js → index.ts} +0 -0
- /package/src/phoneNumberInput/utils/isStringNumeric/{index.js → index.ts} +0 -0
- /package/src/phoneNumberInput/utils/isValidPhoneNumber/{index.js → index.ts} +0 -0
- /package/src/phoneNumberInput/utils/longestMatchingPrefix/{longestMatchingPrefix.spec.js → longestMatchingPrefix.spec.ts} +0 -0
- /package/src/phoneNumberInput/utils/setDefaultPrefix/{setDefaultPrefix.spec.js → setDefaultPrefix.spec.ts} +0 -0
- /package/src/phoneNumberInput/utils/sortArrayByProperty/{index.js → index.ts} +0 -0
- /package/src/phoneNumberInput/utils/sortArrayByProperty/{sortArrayByProperty.spec.js → sortArrayByProperty.spec.ts} +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
|
|
4
|
+
import { Size, SizeLarge, SizeMedium, SizeSmall } from '../common';
|
|
5
|
+
import { SelectInput, SelectInputOptionContent, SelectInputProps } from '../inputs/SelectInput';
|
|
6
|
+
|
|
7
|
+
import countries from './data/countries';
|
|
8
|
+
import {
|
|
9
|
+
explodeNumberModel,
|
|
10
|
+
isValidPhoneNumber,
|
|
11
|
+
cleanNumber,
|
|
12
|
+
setDefaultPrefix,
|
|
13
|
+
sortArrayByProperty,
|
|
14
|
+
groupCountriesByPrefix,
|
|
15
|
+
excludeCountries,
|
|
16
|
+
} from './utils';
|
|
17
|
+
import { PhoneNumber } from './utils/explodeNumberModel';
|
|
18
|
+
|
|
19
|
+
const ALLOWED_PHONE_CHARS = /^$|^[\d-\s]+$/;
|
|
20
|
+
|
|
21
|
+
export interface PhoneNumberPrefixOptionItem {
|
|
22
|
+
header?: never;
|
|
23
|
+
value: string;
|
|
24
|
+
label: string;
|
|
25
|
+
currency: string;
|
|
26
|
+
note?: string;
|
|
27
|
+
searchable?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PhoneNumberInputProps {
|
|
31
|
+
id?: string;
|
|
32
|
+
required?: boolean;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
initialValue?: string;
|
|
35
|
+
onChange: (value: string | null, prefix: string) => void;
|
|
36
|
+
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
|
37
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
38
|
+
countryCode?: string;
|
|
39
|
+
searchPlaceholder?: string;
|
|
40
|
+
size?: SizeSmall | SizeMedium | SizeLarge;
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
selectProps?: Partial<SelectInputProps<string | null>>;
|
|
43
|
+
/** List of iso3 codes of countries to remove from the list */
|
|
44
|
+
disabledCountries?: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const defaultSelectProps = {} satisfies PhoneNumberInputProps['selectProps'];
|
|
48
|
+
const defaultDisabledCountries = [] satisfies PhoneNumberInputProps['disabledCountries'];
|
|
49
|
+
|
|
50
|
+
const PhoneNumberInput = ({
|
|
51
|
+
id,
|
|
52
|
+
required,
|
|
53
|
+
disabled,
|
|
54
|
+
initialValue,
|
|
55
|
+
onChange,
|
|
56
|
+
onFocus,
|
|
57
|
+
onBlur,
|
|
58
|
+
countryCode,
|
|
59
|
+
searchPlaceholder = 'Prefix',
|
|
60
|
+
size = Size.MEDIUM,
|
|
61
|
+
placeholder,
|
|
62
|
+
selectProps = defaultSelectProps,
|
|
63
|
+
disabledCountries = defaultDisabledCountries,
|
|
64
|
+
}: PhoneNumberInputProps) => {
|
|
65
|
+
const { locale } = useIntl();
|
|
66
|
+
|
|
67
|
+
const [internalValue, setInternalValue] = useState<PhoneNumber>(() => {
|
|
68
|
+
const cleanValue = initialValue ? cleanNumber(initialValue) : null;
|
|
69
|
+
|
|
70
|
+
if (!cleanValue || !isValidPhoneNumber(cleanValue)) {
|
|
71
|
+
return {
|
|
72
|
+
prefix: setDefaultPrefix(locale, countryCode),
|
|
73
|
+
suffix: '',
|
|
74
|
+
format: undefined,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return explodeNumberModel(cleanValue);
|
|
79
|
+
});
|
|
80
|
+
const [broadcastedValue, setBroadcastedValue] = useState<PhoneNumber | null>(null);
|
|
81
|
+
|
|
82
|
+
const countriesByPrefix = useMemo(
|
|
83
|
+
() =>
|
|
84
|
+
groupCountriesByPrefix(
|
|
85
|
+
sortArrayByProperty(excludeCountries(countries, disabledCountries), 'iso3'),
|
|
86
|
+
),
|
|
87
|
+
[disabledCountries],
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const onSuffixChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
91
|
+
const suffix = event.target.value;
|
|
92
|
+
if (ALLOWED_PHONE_CHARS.test(suffix)) {
|
|
93
|
+
setInternalValue((prev) => ({ ...prev, suffix }));
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const onPaste: React.ClipboardEventHandler<HTMLInputElement> = (event) => {
|
|
98
|
+
if (!event.nativeEvent.clipboardData) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const pastedValue = (event.nativeEvent.clipboardData.getData('text/plain') || '').replace(
|
|
103
|
+
/(\s|-)+/g,
|
|
104
|
+
'',
|
|
105
|
+
);
|
|
106
|
+
const pastedNumber = explodeNumberModel(pastedValue);
|
|
107
|
+
|
|
108
|
+
if (
|
|
109
|
+
pastedNumber.prefix != null &&
|
|
110
|
+
countriesByPrefix.has(pastedNumber.prefix) &&
|
|
111
|
+
ALLOWED_PHONE_CHARS.test(pastedNumber.suffix)
|
|
112
|
+
) {
|
|
113
|
+
setInternalValue(pastedNumber);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (broadcastedValue === null) {
|
|
119
|
+
return setBroadcastedValue(internalValue);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const internalPhoneNumber = `${internalValue.prefix ?? ''}${internalValue.suffix}`;
|
|
123
|
+
const broadcastedPhoneNumber = `${broadcastedValue.prefix ?? ''}${broadcastedValue.suffix}`;
|
|
124
|
+
|
|
125
|
+
if (internalPhoneNumber === broadcastedPhoneNumber) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const newValue = isValidPhoneNumber(internalPhoneNumber)
|
|
130
|
+
? cleanNumber(internalPhoneNumber)
|
|
131
|
+
: null;
|
|
132
|
+
|
|
133
|
+
onChange(
|
|
134
|
+
newValue,
|
|
135
|
+
internalValue.prefix ?? '', // TODO: Allow `null` in public API
|
|
136
|
+
);
|
|
137
|
+
setBroadcastedValue(internalValue);
|
|
138
|
+
}, [onChange, broadcastedValue, internalValue]);
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<div className="tw-telephone">
|
|
142
|
+
<div className="tw-telephone__country-select">
|
|
143
|
+
<SelectInput
|
|
144
|
+
placeholder="Select an option…"
|
|
145
|
+
items={[...countriesByPrefix].map(([prefix, countries]) => ({
|
|
146
|
+
type: 'option',
|
|
147
|
+
value: prefix,
|
|
148
|
+
filterMatchers: [
|
|
149
|
+
prefix,
|
|
150
|
+
...countries.map((country) => country.name),
|
|
151
|
+
...countries.map((country) => country.iso3),
|
|
152
|
+
],
|
|
153
|
+
}))}
|
|
154
|
+
value={internalValue.prefix}
|
|
155
|
+
renderValue={(prefix, withinTrigger) => (
|
|
156
|
+
<SelectInputOptionContent
|
|
157
|
+
title={prefix}
|
|
158
|
+
note={
|
|
159
|
+
withinTrigger
|
|
160
|
+
? undefined
|
|
161
|
+
: countriesByPrefix
|
|
162
|
+
.get(prefix)
|
|
163
|
+
?.map((country) => country.iso3)
|
|
164
|
+
.join(', ')
|
|
165
|
+
}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
filterable
|
|
169
|
+
filterPlaceholder={searchPlaceholder}
|
|
170
|
+
disabled={disabled}
|
|
171
|
+
size={size}
|
|
172
|
+
onChange={(prefix) => {
|
|
173
|
+
setInternalValue((prev) => ({ ...prev, prefix }));
|
|
174
|
+
}}
|
|
175
|
+
{...selectProps}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
<div className="tw-telephone__number-input">
|
|
179
|
+
<div className={`input-group input-group-${size}`}>
|
|
180
|
+
<input
|
|
181
|
+
id={id}
|
|
182
|
+
autoComplete="tel-national"
|
|
183
|
+
name="phoneNumber"
|
|
184
|
+
inputMode="numeric"
|
|
185
|
+
value={internalValue.suffix}
|
|
186
|
+
className="form-control"
|
|
187
|
+
disabled={disabled}
|
|
188
|
+
required={required}
|
|
189
|
+
placeholder={placeholder}
|
|
190
|
+
onChange={onSuffixChange}
|
|
191
|
+
onPaste={onPaste}
|
|
192
|
+
onFocus={onFocus}
|
|
193
|
+
onBlur={onBlur}
|
|
194
|
+
/>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export default PhoneNumberInput;
|
|
@@ -4,7 +4,7 @@ import { excludeCountries } from './excludeCountries';
|
|
|
4
4
|
|
|
5
5
|
describe('Exclude countries', () => {
|
|
6
6
|
it('should return all the countries of list is empty', () => {
|
|
7
|
-
const remove = [];
|
|
7
|
+
const remove: string[] = [];
|
|
8
8
|
const filteredCountries = excludeCountries(countries, remove);
|
|
9
9
|
expect(filteredCountries).toHaveLength(countries.length);
|
|
10
10
|
});
|
package/src/phoneNumberInput/utils/excludeCountries/{excludeCountries.js → excludeCountries.ts}
RENAMED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { Country } from '../../data/countries';
|
|
2
|
+
|
|
1
3
|
// Reference fro localeCompare : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
|
|
2
|
-
const filterCountriesByIso3 = (countries, iso3Codes) => {
|
|
4
|
+
const filterCountriesByIso3 = (countries: Country[], iso3Codes: string[]) => {
|
|
3
5
|
const iso3CodesSet = new Set(iso3Codes);
|
|
4
6
|
return countries.filter((country) => !iso3CodesSet.has(country.iso3));
|
|
5
7
|
};
|
|
@@ -7,11 +9,10 @@ const filterCountriesByIso3 = (countries, iso3Codes) => {
|
|
|
7
9
|
/**
|
|
8
10
|
* Removes the countries sepecified in the second param
|
|
9
11
|
*
|
|
10
|
-
* @param
|
|
11
|
-
* @param
|
|
12
|
-
* @returns
|
|
12
|
+
* @param countries list of country metadata objects
|
|
13
|
+
* @param disabledCountries list of iso3 country codes to remove from the list
|
|
13
14
|
*/
|
|
14
|
-
export const excludeCountries = (countries, disabledCountries) => {
|
|
15
|
+
export const excludeCountries = (countries: Country[], disabledCountries: string[]) => {
|
|
15
16
|
return disabledCountries.length > 0
|
|
16
17
|
? filterCountriesByIso3(countries, disabledCountries)
|
|
17
18
|
: countries;
|
|
@@ -12,7 +12,7 @@ describe('explodeNumberModel', () => {
|
|
|
12
12
|
|
|
13
13
|
it('should return an exploded number for three digit prefix', () => {
|
|
14
14
|
expect(explodeNumberModel('+3727573135343')).toStrictEqual({
|
|
15
|
-
format:
|
|
15
|
+
format: undefined,
|
|
16
16
|
prefix: '+372',
|
|
17
17
|
suffix: '7573135343',
|
|
18
18
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { findCountryByPrefix } from '../findCountryByPrefix';
|
|
2
|
+
|
|
3
|
+
export interface PhoneNumber {
|
|
4
|
+
prefix: string | null;
|
|
5
|
+
suffix: string;
|
|
6
|
+
format: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param number Phone number in a format like "+447573135343"
|
|
11
|
+
*/
|
|
12
|
+
export const explodeNumberModel = (number: string): PhoneNumber => {
|
|
13
|
+
const country = findCountryByPrefix(number);
|
|
14
|
+
return country
|
|
15
|
+
? {
|
|
16
|
+
prefix: country.phone,
|
|
17
|
+
suffix: number.slice(country.phone.length),
|
|
18
|
+
format: country.phoneFormat,
|
|
19
|
+
}
|
|
20
|
+
: {
|
|
21
|
+
prefix: null,
|
|
22
|
+
suffix: number.slice(1),
|
|
23
|
+
format: undefined,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -14,7 +14,6 @@ describe('findCountryByCode', () => {
|
|
|
14
14
|
|
|
15
15
|
it('should return null for invalid code', () => {
|
|
16
16
|
expect(findCountryByCode('Wrong')).toBeNull();
|
|
17
|
-
expect(findCountryByCode(null)).toBeNull();
|
|
18
17
|
expect(findCountryByCode('')).toBeNull();
|
|
19
18
|
expect(findCountryByCode(' ')).toBeNull();
|
|
20
19
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import countries from '../../data/countries';
|
|
2
|
+
import { longestMatchingPrefix } from '../longestMatchingPrefix';
|
|
3
|
+
|
|
4
|
+
export const findCountryByCode = (code: string) => {
|
|
5
|
+
if (code.length === 2) {
|
|
6
|
+
const matchingCodes = countries.filter((country) => code.toUpperCase() === country.iso2);
|
|
7
|
+
if (matchingCodes.length > 0) {
|
|
8
|
+
return longestMatchingPrefix(matchingCodes);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import countries from '../../data/countries';
|
|
2
|
+
import { longestMatchingPrefix } from '../longestMatchingPrefix';
|
|
3
|
+
|
|
4
|
+
export const findCountryByPrefix = (number: string) => {
|
|
5
|
+
if (number.length > 1) {
|
|
6
|
+
const matchingCodes = countries.filter((country) => number.startsWith(country.phone));
|
|
7
|
+
if (matchingCodes.length > 0) {
|
|
8
|
+
return longestMatchingPrefix(matchingCodes);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Country } from '../../data/countries';
|
|
2
|
+
|
|
3
|
+
import { groupCountriesByPrefix } from '.';
|
|
4
|
+
|
|
5
|
+
const countries = [
|
|
6
|
+
{
|
|
7
|
+
name: 'Canada',
|
|
8
|
+
iso2: 'CA',
|
|
9
|
+
iso3: 'CAN',
|
|
10
|
+
phone: '+1',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'United States of America',
|
|
14
|
+
iso2: 'US',
|
|
15
|
+
iso3: 'USA',
|
|
16
|
+
phone: '+1',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'United States Minor Outlying Islands',
|
|
20
|
+
iso2: 'UM',
|
|
21
|
+
iso3: 'UMI',
|
|
22
|
+
phone: '+1',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'United Kingdom',
|
|
26
|
+
iso2: 'GB',
|
|
27
|
+
iso3: 'GBR',
|
|
28
|
+
phone: '+44',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Guernsey',
|
|
32
|
+
iso2: 'GG',
|
|
33
|
+
iso3: 'GGY',
|
|
34
|
+
phone: '+44',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Guinea',
|
|
38
|
+
iso2: 'GN',
|
|
39
|
+
iso3: 'GIN',
|
|
40
|
+
phone: '+224',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const groupedCountries = new Map<string, Country[]>([
|
|
45
|
+
[
|
|
46
|
+
'+1',
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
name: 'Canada',
|
|
50
|
+
iso2: 'CA',
|
|
51
|
+
iso3: 'CAN',
|
|
52
|
+
phone: '+1',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'United States of America',
|
|
56
|
+
iso2: 'US',
|
|
57
|
+
iso3: 'USA',
|
|
58
|
+
phone: '+1',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'United States Minor Outlying Islands',
|
|
62
|
+
iso2: 'UM',
|
|
63
|
+
iso3: 'UMI',
|
|
64
|
+
phone: '+1',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
'+44',
|
|
70
|
+
[
|
|
71
|
+
{
|
|
72
|
+
name: 'United Kingdom',
|
|
73
|
+
iso2: 'GB',
|
|
74
|
+
iso3: 'GBR',
|
|
75
|
+
phone: '+44',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Guernsey',
|
|
79
|
+
iso2: 'GG',
|
|
80
|
+
iso3: 'GGY',
|
|
81
|
+
phone: '+44',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
'+224',
|
|
87
|
+
[
|
|
88
|
+
{
|
|
89
|
+
name: 'Guinea',
|
|
90
|
+
iso2: 'GN',
|
|
91
|
+
iso3: 'GIN',
|
|
92
|
+
phone: '+224',
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
],
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
describe('groupCountriesByPrefix', () => {
|
|
99
|
+
it('groups countries by prefix', () => {
|
|
100
|
+
expect(groupCountriesByPrefix(countries)).toStrictEqual(groupedCountries);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Country } from '../../data/countries';
|
|
2
|
+
|
|
3
|
+
export const groupCountriesByPrefix = (countries: Country[]) => {
|
|
4
|
+
const countriesByPrefix = new Map<string, Country[]>();
|
|
5
|
+
countries.forEach((country) => {
|
|
6
|
+
countriesByPrefix.set(country.phone, [
|
|
7
|
+
...(countriesByPrefix.get(country.phone) ?? []),
|
|
8
|
+
country,
|
|
9
|
+
]);
|
|
10
|
+
});
|
|
11
|
+
return countriesByPrefix;
|
|
12
|
+
};
|
|
@@ -4,8 +4,6 @@ export { explodeNumberModel } from './explodeNumberModel';
|
|
|
4
4
|
export { longestMatchingPrefix } from './longestMatchingPrefix';
|
|
5
5
|
export { findCountryByPrefix } from './findCountryByPrefix';
|
|
6
6
|
export { findCountryByCode } from './findCountryByCode';
|
|
7
|
-
export { filterOptionsForQuery } from './filterOptionsForQuery';
|
|
8
|
-
export { isOptionAndFitsQuery } from './isOptionAndFitsQuery';
|
|
9
7
|
export { cleanNumber } from './cleanNumber';
|
|
10
8
|
export { isStringNumeric } from './isStringNumeric';
|
|
11
9
|
export { sortArrayByProperty } from './sortArrayByProperty';
|
|
@@ -4,7 +4,6 @@ describe('isStringNumeric', () => {
|
|
|
4
4
|
it('should return true when numeric sting is provided', () => {
|
|
5
5
|
expect(isStringNumeric('+23456')).toBe(true);
|
|
6
6
|
expect(isStringNumeric('23456')).toBe(true);
|
|
7
|
-
expect(isStringNumeric(23456)).toBe(true);
|
|
8
7
|
});
|
|
9
8
|
|
|
10
9
|
it('should return false when string is provided', () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const isStringNumeric = (value: string) => /^\+?[\d-\s]+$/.test(value);
|
|
@@ -11,7 +11,7 @@ describe('isValidPhoneNumber', () => {
|
|
|
11
11
|
|
|
12
12
|
it('should return false for invalid numbers', () => {
|
|
13
13
|
expect(isValidPhoneNumber('+441')).toBe(false);
|
|
14
|
-
expect(isValidPhoneNumber(44)).toBe(false);
|
|
14
|
+
expect(isValidPhoneNumber('44')).toBe(false);
|
|
15
15
|
expect(isValidPhoneNumber('44123')).toBe(false);
|
|
16
16
|
});
|
|
17
17
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param phoneNumber
|
|
4
|
+
* @returns True if number that starts with "+"" and contains a mix of digits and spaces with at least 4 digits.
|
|
5
|
+
*/
|
|
6
|
+
export const isValidPhoneNumber = (phoneNumber: string) =>
|
|
7
|
+
/^\+[\d-\s]+$/.test(phoneNumber) && (phoneNumber.match(/\d+/g)?.join('').length ?? 0) >= 4;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getCountryFromLocale } from '../../../common/locale';
|
|
2
|
+
import { findCountryByCode } from '../findCountryByCode';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default phone code, the UK one `+44`
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_PHONE_CODE = '+44';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Given a valid locale it returns the correspondent prefix if found or +44 otherwise.
|
|
11
|
+
*
|
|
12
|
+
* @param locale BCP 47 language tag of locale.
|
|
13
|
+
* @param countryCode Two-letter country code (ISO 3166-1 alpha-2).
|
|
14
|
+
*/
|
|
15
|
+
export const setDefaultPrefix = (locale: string, countryCode?: string) => {
|
|
16
|
+
const country =
|
|
17
|
+
(countryCode != null ? findCountryByCode(countryCode) : null) ??
|
|
18
|
+
findCountryByCode(getCountryFromLocale(locale) ?? locale);
|
|
19
|
+
return country?.phone ?? DEFAULT_PHONE_CODE;
|
|
20
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
|
|
3
|
+
import WithDisplayFormat from '../withDisplayFormat';
|
|
4
|
+
|
|
5
|
+
const TextareaWithDisplayFormat = (props) => (
|
|
6
|
+
<WithDisplayFormat {...props} render={(renderProps) => <textarea {...renderProps} />} />
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
TextareaWithDisplayFormat.propTypes = {
|
|
10
|
+
displayPattern: PropTypes.string.isRequired,
|
|
11
|
+
onChange: PropTypes.func.isRequired,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default TextareaWithDisplayFormat;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { shallow } from 'enzyme';
|
|
2
2
|
|
|
3
|
-
import { TextArea } from '../inputs/TextArea';
|
|
4
|
-
|
|
5
3
|
import TextareaWithDisplayFormat from '.';
|
|
6
4
|
|
|
7
5
|
describe('TextareaWithDisplayFormat', () => {
|
|
@@ -12,6 +10,6 @@ describe('TextareaWithDisplayFormat', () => {
|
|
|
12
10
|
.find('WithDisplayFormat')
|
|
13
11
|
.renderProp('render')({ value: 'test' });
|
|
14
12
|
|
|
15
|
-
expect(view.find(
|
|
13
|
+
expect(view.find('textarea').props('value')).toStrictEqual({ value: 'test' });
|
|
16
14
|
});
|
|
17
15
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './TextareaWithDisplayFormat';
|