@transferwise/components 0.0.0-experimental-85b9dd1 → 0.0.0-experimental-0db2ae7
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 +335 -234
- package/build/index.esm.js.map +1 -1
- package/build/index.js +336 -235
- package/build/index.js.map +1 -1
- package/build/types/common/locale/index.d.ts +43 -26
- 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 -2
- package/build/types/common/textFormat/getCursorPositionAfterKeystroke/getCursorPositionAfterKeystroke.d.ts.map +1 -1
- package/build/types/common/textFormat/getSymbolsInPatternWithPosition/getSymbolsInPatternWithPosition.d.ts +5 -1
- 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 +2 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +7 -11
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/index.d.ts +2 -1
- package/build/types/inputWithDisplayFormat/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts +27 -22
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/phoneNumberInput/data/countries.d.ts +10 -5
- 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 +1 -8
- 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 +4 -8
- package/build/types/phoneNumberInput/utils/explodeNumberModel/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/filterOptionsForQuery/index.d.ts +2 -0
- package/build/types/phoneNumberInput/utils/filterOptionsForQuery/index.d.ts.map +1 -0
- 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 +1 -2
- 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 +13 -11
- package/build/types/phoneNumberInput/utils/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/index.d.ts +2 -0
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/index.d.ts.map +1 -0
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.d.ts +3 -0
- package/build/types/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.d.ts.map +1 -0
- 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 +1 -6
- package/build/types/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/longestMatchingPrefix/index.d.ts +1 -2
- package/build/types/phoneNumberInput/utils/longestMatchingPrefix/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/utils/setDefaultPrefix/index.d.ts +1 -7
- 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 +7 -11
- package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts.map +1 -1
- package/build/types/textareaWithDisplayFormat/index.d.ts +2 -1
- package/build/types/textareaWithDisplayFormat/index.d.ts.map +1 -1
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts +54 -82
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
- package/build/types/withDisplayFormat/index.d.ts +2 -1
- package/build/types/withDisplayFormat/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/locale/index.js +139 -0
- package/src/common/locale/{index.spec.ts → index.spec.js} +4 -4
- package/src/common/textFormat/formatWithPattern/{formatWithPattern.js → formatWithPattern.ts} +8 -4
- package/src/common/textFormat/getCursorPositionAfterKeystroke/{getCursorPositionAfterKeystroke.js → getCursorPositionAfterKeystroke.ts} +8 -8
- package/src/common/textFormat/getSymbolsInPatternWithPosition/{getSymbolsInPatternWithPosition.js → getSymbolsInPatternWithPosition.ts} +7 -2
- package/src/common/textFormat/unformatWithPattern/{unformatWithPattern.js → unformatWithPattern.ts} +3 -2
- package/src/index.ts +2 -0
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +10 -0
- package/src/inputWithDisplayFormat/index.ts +2 -0
- package/src/phoneNumberInput/PhoneNumberInput.js +210 -0
- package/src/phoneNumberInput/PhoneNumberInput.spec.js +22 -18
- package/src/phoneNumberInput/data/{countries.ts → countries.js} +1 -9
- package/src/phoneNumberInput/data/countries.spec.js +12 -0
- package/src/phoneNumberInput/utils/cleanNumber/cleanNumber.js +4 -0
- package/src/phoneNumberInput/utils/excludeCountries/{excludeCountries.ts → excludeCountries.js} +5 -6
- package/src/phoneNumberInput/utils/excludeCountries/{excludeCountries.spec.ts → excludeCountries.spec.js} +1 -1
- package/src/phoneNumberInput/utils/explodeNumberModel/{explodeNumberModel.spec.ts → explodeNumberModel.spec.js} +1 -1
- package/src/phoneNumberInput/utils/explodeNumberModel/index.js +27 -0
- package/src/phoneNumberInput/utils/filterOptionsForQuery/filterOptionsForQuery.spec.js +36 -0
- package/src/phoneNumberInput/utils/filterOptionsForQuery/index.js +11 -0
- package/src/phoneNumberInput/utils/findCountryByCode/{findCountryByCode.spec.ts → findCountryByCode.spec.js} +1 -0
- package/src/phoneNumberInput/utils/findCountryByCode/index.js +10 -0
- package/src/phoneNumberInput/utils/findCountryByPrefix/index.js +11 -0
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.js +26 -0
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.spec.js +67 -0
- package/src/phoneNumberInput/utils/{index.ts → index.js} +2 -0
- package/src/phoneNumberInput/utils/isOptionAndFitsQuery/index.js +1 -0
- package/src/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.js +25 -0
- package/src/phoneNumberInput/utils/isOptionAndFitsQuery/isOptionAndFitsQuery.spec.js +66 -0
- package/src/phoneNumberInput/utils/isStringNumeric/isStringNumeric.js +1 -0
- package/src/phoneNumberInput/utils/isStringNumeric/{isStringNumeric.spec.ts → isStringNumeric.spec.js} +1 -0
- package/src/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.js +10 -0
- package/src/phoneNumberInput/utils/isValidPhoneNumber/{isValidPhoneNumber.spec.ts → isValidPhoneNumber.spec.js} +1 -1
- package/src/phoneNumberInput/utils/longestMatchingPrefix/index.js +2 -0
- package/src/phoneNumberInput/utils/setDefaultPrefix/index.js +25 -0
- package/src/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.js +3 -0
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.spec.js +3 -1
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +32 -0
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +13 -0
- package/src/textareaWithDisplayFormat/index.ts +2 -0
- package/src/withDisplayFormat/WithDisplayFormat.spec.js +1 -1
- package/src/withDisplayFormat/{WithDisplayFormat.js → WithDisplayFormat.tsx} +127 -107
- package/src/withDisplayFormat/index.ts +2 -0
- package/src/common/locale/index.ts +0 -96
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.js +0 -14
- package/src/inputWithDisplayFormat/index.js +0 -1
- package/src/phoneNumberInput/PhoneNumberInput.tsx +0 -193
- package/src/phoneNumberInput/utils/cleanNumber/cleanNumber.ts +0 -3
- package/src/phoneNumberInput/utils/explodeNumberModel/index.ts +0 -24
- package/src/phoneNumberInput/utils/findCountryByCode/index.ts +0 -12
- package/src/phoneNumberInput/utils/findCountryByPrefix/index.ts +0 -12
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.spec.ts +0 -102
- package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.ts +0 -12
- package/src/phoneNumberInput/utils/isStringNumeric/isStringNumeric.ts +0 -1
- package/src/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.ts +0 -7
- package/src/phoneNumberInput/utils/longestMatchingPrefix/index.ts +0 -4
- package/src/phoneNumberInput/utils/setDefaultPrefix/index.ts +0 -20
- package/src/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.ts +0 -6
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.js +0 -14
- package/src/textareaWithDisplayFormat/index.js +0 -1
- package/src/withDisplayFormat/index.js +0 -1
- /package/src/phoneNumberInput/{PhoneNumberInput.story.tsx → PhoneNumberInput.story.js} +0 -0
- /package/src/phoneNumberInput/{index.ts → index.js} +0 -0
- /package/src/phoneNumberInput/utils/cleanNumber/{cleanNumber.spec.ts → cleanNumber.spec.js} +0 -0
- /package/src/phoneNumberInput/utils/cleanNumber/{index.ts → index.js} +0 -0
- /package/src/phoneNumberInput/utils/excludeCountries/{index.ts → index.js} +0 -0
- /package/src/phoneNumberInput/utils/findCountryByPrefix/{findCountryByPrefix.spec.ts → findCountryByPrefix.spec.js} +0 -0
- /package/src/phoneNumberInput/utils/groupCountriesByPrefix/{index.ts → index.js} +0 -0
- /package/src/phoneNumberInput/utils/isStringNumeric/{index.ts → index.js} +0 -0
- /package/src/phoneNumberInput/utils/isValidPhoneNumber/{index.ts → index.js} +0 -0
- /package/src/phoneNumberInput/utils/longestMatchingPrefix/{longestMatchingPrefix.spec.ts → longestMatchingPrefix.spec.js} +0 -0
- /package/src/phoneNumberInput/utils/setDefaultPrefix/{setDefaultPrefix.spec.ts → setDefaultPrefix.spec.js} +0 -0
- /package/src/phoneNumberInput/utils/sortArrayByProperty/{index.ts → index.js} +0 -0
- /package/src/phoneNumberInput/utils/sortArrayByProperty/{sortArrayByProperty.spec.ts → sortArrayByProperty.spec.js} +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { isArray } from '@transferwise/neptune-validation';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import { useIntl } from 'react-intl';
|
|
5
|
+
|
|
6
|
+
import { Size } from '../common';
|
|
7
|
+
import { SelectInput, SelectInputOptionContent } from '../inputs/SelectInput';
|
|
8
|
+
|
|
9
|
+
import countries from './data/countries';
|
|
10
|
+
import {
|
|
11
|
+
explodeNumberModel,
|
|
12
|
+
isValidPhoneNumber,
|
|
13
|
+
cleanNumber,
|
|
14
|
+
setDefaultPrefix,
|
|
15
|
+
sortArrayByProperty,
|
|
16
|
+
groupCountriesByPrefix,
|
|
17
|
+
excludeCountries,
|
|
18
|
+
} from './utils';
|
|
19
|
+
|
|
20
|
+
const ALLOWED_PHONE_CHARS = /^$|^[\d-\s]+$/;
|
|
21
|
+
|
|
22
|
+
const PhoneNumberInput = (props) => {
|
|
23
|
+
const {
|
|
24
|
+
id,
|
|
25
|
+
onChange,
|
|
26
|
+
searchPlaceholder,
|
|
27
|
+
disabled,
|
|
28
|
+
required,
|
|
29
|
+
size,
|
|
30
|
+
placeholder,
|
|
31
|
+
onFocus,
|
|
32
|
+
onBlur,
|
|
33
|
+
countryCode,
|
|
34
|
+
selectProps,
|
|
35
|
+
disabledCountries,
|
|
36
|
+
} = props;
|
|
37
|
+
const { locale } = useIntl();
|
|
38
|
+
|
|
39
|
+
const getInitialValue = () => {
|
|
40
|
+
const { initialValue } = props;
|
|
41
|
+
|
|
42
|
+
const cleanValue = initialValue ? cleanNumber(initialValue) : null;
|
|
43
|
+
|
|
44
|
+
if (!cleanValue || !isValidPhoneNumber(cleanValue)) {
|
|
45
|
+
return {
|
|
46
|
+
prefix: setDefaultPrefix(locale, countryCode),
|
|
47
|
+
suffix: '',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return explodeNumberModel(cleanValue);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const [internalValue, setInternalValue] = useState(getInitialValue());
|
|
55
|
+
const [broadcastedValue, setBroadcastedValue] = useState(null);
|
|
56
|
+
|
|
57
|
+
const getSelectOptions = () => {
|
|
58
|
+
const countriesList = excludeCountries(countries, disabledCountries);
|
|
59
|
+
const listSortedByISO3 = groupCountriesByPrefix(sortArrayByProperty(countriesList, 'iso3'));
|
|
60
|
+
|
|
61
|
+
return listSortedByISO3.map((option) => {
|
|
62
|
+
const { phone, iso3, iso2, name } = option;
|
|
63
|
+
let note = '';
|
|
64
|
+
|
|
65
|
+
if (iso3) {
|
|
66
|
+
note = isArray(iso3) ? iso3.join(', ') : iso3;
|
|
67
|
+
} else if (iso2) {
|
|
68
|
+
note = isArray(iso2) ? iso2.join(', ') : iso2;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
type: 'option',
|
|
73
|
+
value: {
|
|
74
|
+
value: phone,
|
|
75
|
+
label: phone,
|
|
76
|
+
note: note,
|
|
77
|
+
},
|
|
78
|
+
filterMatchers: [phone, note, name],
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const options = getSelectOptions();
|
|
84
|
+
|
|
85
|
+
const onPrefixChange = ({ value }) => {
|
|
86
|
+
setInternalValue({ prefix: value, suffix: internalValue.suffix });
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const onSuffixChange = (event) => {
|
|
90
|
+
const { value = '' } = event.target;
|
|
91
|
+
|
|
92
|
+
if (ALLOWED_PHONE_CHARS.test(value)) {
|
|
93
|
+
setInternalValue({ prefix: internalValue.prefix, suffix: value });
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const onPaste = (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 { prefix: pastedPrefix, suffix: pastedSuffix } = explodeNumberModel(pastedValue);
|
|
107
|
+
const selectedPrefix = options.find(({ value }) => value.value === pastedPrefix);
|
|
108
|
+
|
|
109
|
+
if (selectedPrefix && ALLOWED_PHONE_CHARS.test(pastedSuffix)) {
|
|
110
|
+
setInternalValue({ prefix: pastedPrefix, suffix: pastedSuffix });
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (broadcastedValue === null) {
|
|
116
|
+
return setBroadcastedValue(internalValue);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const internalPhoneNumber = internalValue.prefix + internalValue.suffix;
|
|
120
|
+
const broadcastedPhoneNumber = broadcastedValue.prefix + broadcastedValue.suffix;
|
|
121
|
+
|
|
122
|
+
if (internalPhoneNumber === broadcastedPhoneNumber) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const newValue = isValidPhoneNumber(internalPhoneNumber)
|
|
127
|
+
? cleanNumber(internalPhoneNumber)
|
|
128
|
+
: null;
|
|
129
|
+
|
|
130
|
+
onChange(newValue, internalValue.prefix);
|
|
131
|
+
setBroadcastedValue(internalValue);
|
|
132
|
+
}, [onChange, broadcastedValue, internalValue]);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div className="tw-telephone">
|
|
136
|
+
<div className="tw-telephone__country-select">
|
|
137
|
+
<SelectInput
|
|
138
|
+
placeholder="Select an option..."
|
|
139
|
+
items={options}
|
|
140
|
+
value={options.find((item) => item.value.value === internalValue.prefix)?.value}
|
|
141
|
+
renderValue={(option, withinTrigger) => (
|
|
142
|
+
<SelectInputOptionContent
|
|
143
|
+
title={option.label}
|
|
144
|
+
note={withinTrigger ? undefined : option.note}
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
147
|
+
filterable
|
|
148
|
+
filterPlaceholder={searchPlaceholder}
|
|
149
|
+
disabled={disabled}
|
|
150
|
+
size={size}
|
|
151
|
+
onChange={onPrefixChange}
|
|
152
|
+
{...selectProps}
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
<div className="tw-telephone__number-input">
|
|
156
|
+
<div className={`input-group input-group-${size}`}>
|
|
157
|
+
<input
|
|
158
|
+
id={id}
|
|
159
|
+
autoComplete="tel-national"
|
|
160
|
+
name="phoneNumber"
|
|
161
|
+
inputMode="numeric"
|
|
162
|
+
value={internalValue.suffix}
|
|
163
|
+
className="form-control"
|
|
164
|
+
disabled={disabled}
|
|
165
|
+
required={required}
|
|
166
|
+
placeholder={placeholder}
|
|
167
|
+
onChange={onSuffixChange}
|
|
168
|
+
onPaste={onPaste}
|
|
169
|
+
onFocus={onFocus}
|
|
170
|
+
onBlur={onBlur}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
PhoneNumberInput.propTypes = {
|
|
179
|
+
id: PropTypes.string,
|
|
180
|
+
required: PropTypes.bool,
|
|
181
|
+
disabled: PropTypes.bool,
|
|
182
|
+
initialValue: PropTypes.string,
|
|
183
|
+
onChange: PropTypes.func.isRequired,
|
|
184
|
+
onFocus: PropTypes.func,
|
|
185
|
+
onBlur: PropTypes.func,
|
|
186
|
+
countryCode: PropTypes.string,
|
|
187
|
+
searchPlaceholder: PropTypes.string,
|
|
188
|
+
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
189
|
+
placeholder: PropTypes.string,
|
|
190
|
+
selectProps: PropTypes.object,
|
|
191
|
+
/** List of iso3 codes of countries to remove from the list */
|
|
192
|
+
disabledCountries: PropTypes.arrayOf(PropTypes.string),
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
PhoneNumberInput.defaultProps = {
|
|
196
|
+
id: null,
|
|
197
|
+
required: false,
|
|
198
|
+
disabled: false,
|
|
199
|
+
initialValue: null,
|
|
200
|
+
onFocus() {},
|
|
201
|
+
onBlur() {},
|
|
202
|
+
countryCode: null,
|
|
203
|
+
searchPlaceholder: 'Prefix',
|
|
204
|
+
size: Size.MEDIUM,
|
|
205
|
+
placeholder: '',
|
|
206
|
+
selectProps: {},
|
|
207
|
+
disabledCountries: [],
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export default PhoneNumberInput;
|
|
@@ -37,7 +37,11 @@ describe('Given a telephone number component', () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('should set prefix control to default UK value', () => {
|
|
40
|
-
expect(select.props().value).
|
|
40
|
+
expect(select.props().value).toStrictEqual({
|
|
41
|
+
value: '+44',
|
|
42
|
+
note: 'GBR, GGY, IMN, JEY',
|
|
43
|
+
label: '+44',
|
|
44
|
+
});
|
|
41
45
|
});
|
|
42
46
|
|
|
43
47
|
it('should set number control to empty', () => {
|
|
@@ -45,11 +49,11 @@ describe('Given a telephone number component', () => {
|
|
|
45
49
|
});
|
|
46
50
|
|
|
47
51
|
it('should not disable the select', () => {
|
|
48
|
-
expect(select.prop('disabled')).
|
|
52
|
+
expect(select.prop('disabled')).toBe(false);
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
it('should not disable the input', () => {
|
|
52
|
-
expect(input.prop('disabled')).
|
|
56
|
+
expect(input.prop('disabled')).toBe(false);
|
|
53
57
|
});
|
|
54
58
|
});
|
|
55
59
|
|
|
@@ -61,7 +65,7 @@ describe('Given a telephone number component', () => {
|
|
|
61
65
|
});
|
|
62
66
|
|
|
63
67
|
it('should set control values correctly', () => {
|
|
64
|
-
expect(select.props().value).
|
|
68
|
+
expect(select.props().value.value).toStrictEqual('+39');
|
|
65
69
|
expect(input.prop('value')).toBe('123456789');
|
|
66
70
|
});
|
|
67
71
|
});
|
|
@@ -88,8 +92,8 @@ describe('Given a telephone number component', () => {
|
|
|
88
92
|
input = component.find(NUMBER_SELECTOR);
|
|
89
93
|
});
|
|
90
94
|
|
|
91
|
-
it('should render input with
|
|
92
|
-
expect(input.prop('id')).
|
|
95
|
+
it('should render input with null id', () => {
|
|
96
|
+
expect(input.prop('id')).toBeNull();
|
|
93
97
|
});
|
|
94
98
|
});
|
|
95
99
|
|
|
@@ -114,7 +118,7 @@ describe('Given a telephone number component', () => {
|
|
|
114
118
|
it(`${number} code should update the value properly`, () => {
|
|
115
119
|
simulatePaste(component.find('input'), number);
|
|
116
120
|
|
|
117
|
-
expect(select().props().value).
|
|
121
|
+
expect(select().props().value.value).toStrictEqual(countryCode);
|
|
118
122
|
expect(input().prop('value')).toBe(localNumber);
|
|
119
123
|
expect(props.onChange).toHaveBeenCalledWith(number.replace(/(\s|-)+/g, ''), countryCode);
|
|
120
124
|
});
|
|
@@ -122,28 +126,28 @@ describe('Given a telephone number component', () => {
|
|
|
122
126
|
|
|
123
127
|
it('should not paste invalid characters', () => {
|
|
124
128
|
simulatePaste(component.find('input'), '+36asdasdasd');
|
|
125
|
-
expect(select().props().value).
|
|
129
|
+
expect(select().props().value.value).toStrictEqual('+39');
|
|
126
130
|
expect(input().prop('value')).toBe('123456789');
|
|
127
131
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
128
132
|
});
|
|
129
133
|
|
|
130
134
|
it('should not paste countries which are not in the select', () => {
|
|
131
135
|
simulatePaste(component.find('input'), '+9992342343423');
|
|
132
|
-
expect(select().props().value).
|
|
136
|
+
expect(select().props().value.value).toStrictEqual('+39');
|
|
133
137
|
expect(input().prop('value')).toBe('123456789');
|
|
134
138
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
135
139
|
});
|
|
136
140
|
|
|
137
141
|
it("should not paste numbers which doesn't start with the country code", () => {
|
|
138
142
|
simulatePaste(component.find('input'), '0+36303932551');
|
|
139
|
-
expect(select().props().value).
|
|
143
|
+
expect(select().props().value.value).toStrictEqual('+39');
|
|
140
144
|
expect(input().prop('value')).toBe('123456789');
|
|
141
145
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
142
146
|
});
|
|
143
147
|
|
|
144
148
|
it("should not paste numbers which doesn't contain a country code", () => {
|
|
145
149
|
simulatePaste(component.find('input'), '06303932551');
|
|
146
|
-
expect(select().props().value).
|
|
150
|
+
expect(select().props().value.value).toStrictEqual('+39');
|
|
147
151
|
expect(input().prop('value')).toBe('123456789');
|
|
148
152
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
149
153
|
});
|
|
@@ -157,7 +161,7 @@ describe('Given a telephone number component', () => {
|
|
|
157
161
|
});
|
|
158
162
|
|
|
159
163
|
it('should set the select to the longest matching prefix', () => {
|
|
160
|
-
expect(select.props().value).
|
|
164
|
+
expect(select.props().value.value).toStrictEqual('+1868');
|
|
161
165
|
});
|
|
162
166
|
|
|
163
167
|
it('should set the number input to the rest of the number', () => {
|
|
@@ -173,7 +177,7 @@ describe('Given a telephone number component', () => {
|
|
|
173
177
|
});
|
|
174
178
|
|
|
175
179
|
it('should empty the select', () => {
|
|
176
|
-
expect(select.props().value).
|
|
180
|
+
expect(select.props().value).toBeUndefined();
|
|
177
181
|
});
|
|
178
182
|
|
|
179
183
|
it('should put the whole value in the input without the plus', () => {
|
|
@@ -187,7 +191,7 @@ describe('Given a telephone number component', () => {
|
|
|
187
191
|
select = component.find(PREFIX_SELECT_SELECTOR);
|
|
188
192
|
input = component.find(NUMBER_SELECTOR);
|
|
189
193
|
|
|
190
|
-
expect(select.props().value).
|
|
194
|
+
expect(select.props().value.value).toStrictEqual('+44');
|
|
191
195
|
expect(input.prop('value')).toBe('');
|
|
192
196
|
});
|
|
193
197
|
});
|
|
@@ -250,7 +254,7 @@ describe('Given a telephone number component', () => {
|
|
|
250
254
|
});
|
|
251
255
|
|
|
252
256
|
it('should use the prefix of the supplied value', () => {
|
|
253
|
-
expect(select.props().value).toBe('+1');
|
|
257
|
+
expect(select.props().value.value).toBe('+1');
|
|
254
258
|
});
|
|
255
259
|
});
|
|
256
260
|
|
|
@@ -264,7 +268,7 @@ describe('Given a telephone number component', () => {
|
|
|
264
268
|
});
|
|
265
269
|
|
|
266
270
|
it('should default the prefix to the local country', () => {
|
|
267
|
-
expect(select.props().value).toBe('+34');
|
|
271
|
+
expect(select.props().value.value).toBe('+34');
|
|
268
272
|
});
|
|
269
273
|
});
|
|
270
274
|
|
|
@@ -277,7 +281,7 @@ describe('Given a telephone number component', () => {
|
|
|
277
281
|
});
|
|
278
282
|
|
|
279
283
|
it('should override locale prefix with country specific prefix', () => {
|
|
280
|
-
expect(select.props().value).toBe('+1');
|
|
284
|
+
expect(select.props().value.value).toBe('+1');
|
|
281
285
|
});
|
|
282
286
|
});
|
|
283
287
|
});
|
|
@@ -345,7 +349,7 @@ describe('Given a telephone number component', () => {
|
|
|
345
349
|
|
|
346
350
|
it('renders Select component with expected props', () => {
|
|
347
351
|
const select = component.find(PREFIX_SELECT_SELECTOR);
|
|
348
|
-
expect(select.prop('className')).
|
|
352
|
+
expect(select.prop('className')).toStrictEqual('custom-class');
|
|
349
353
|
});
|
|
350
354
|
});
|
|
351
355
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import countries from './countries';
|
|
2
|
+
|
|
3
|
+
describe('Given a list of countries', () => {
|
|
4
|
+
countries.forEach((country) => {
|
|
5
|
+
it('each country should have a valid format', () => {
|
|
6
|
+
expect(country).toHaveProperty('phone');
|
|
7
|
+
expect(country).toHaveProperty('name');
|
|
8
|
+
expect(country).toHaveProperty('iso3');
|
|
9
|
+
expect(country).toHaveProperty('iso2');
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
});
|
package/src/phoneNumberInput/utils/excludeCountries/{excludeCountries.ts → excludeCountries.js}
RENAMED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import type { Country } from '../../data/countries';
|
|
2
|
-
|
|
3
1
|
// Reference fro localeCompare : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
|
|
4
|
-
const filterCountriesByIso3 = (countries
|
|
2
|
+
const filterCountriesByIso3 = (countries, iso3Codes) => {
|
|
5
3
|
const iso3CodesSet = new Set(iso3Codes);
|
|
6
4
|
return countries.filter((country) => !iso3CodesSet.has(country.iso3));
|
|
7
5
|
};
|
|
@@ -9,10 +7,11 @@ const filterCountriesByIso3 = (countries: Country[], iso3Codes: string[]) => {
|
|
|
9
7
|
/**
|
|
10
8
|
* Removes the countries sepecified in the second param
|
|
11
9
|
*
|
|
12
|
-
* @param countries list of country metadata objects
|
|
13
|
-
* @param disabledCountries list of iso3 country codes to remove from the list
|
|
10
|
+
* @param {Array} countries: list of country metadata objects
|
|
11
|
+
* @param {Array} disabledCountries: list of iso3 country codes to remove from the list
|
|
12
|
+
* @returns
|
|
14
13
|
*/
|
|
15
|
-
export const excludeCountries = (countries
|
|
14
|
+
export const excludeCountries = (countries, disabledCountries) => {
|
|
16
15
|
return disabledCountries.length > 0
|
|
17
16
|
? filterCountriesByIso3(countries, disabledCountries)
|
|
18
17
|
: countries;
|
|
@@ -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 = [];
|
|
8
8
|
const filteredCountries = excludeCountries(countries, remove);
|
|
9
9
|
expect(filteredCountries).toHaveLength(countries.length);
|
|
10
10
|
});
|
|
@@ -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: '',
|
|
16
16
|
prefix: '+372',
|
|
17
17
|
suffix: '7573135343',
|
|
18
18
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { findCountryByPrefix } from '../findCountryByPrefix';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Given a sting in a valid format ex:'+447573135343' it returns an object of shape
|
|
5
|
+
* {prefix=+44 ,suffix=7573135343}
|
|
6
|
+
*
|
|
7
|
+
* @param {string} number - a string that defines a phone number.
|
|
8
|
+
* @returns {{prefix: (string|*), suffix: string, format: string}}
|
|
9
|
+
*/
|
|
10
|
+
export const explodeNumberModel = (number) => {
|
|
11
|
+
let prefix = '';
|
|
12
|
+
let suffix = '';
|
|
13
|
+
let format = '';
|
|
14
|
+
const country = findCountryByPrefix(number);
|
|
15
|
+
|
|
16
|
+
if (country) {
|
|
17
|
+
prefix = country.phone;
|
|
18
|
+
suffix = number.slice(country.phone.length);
|
|
19
|
+
format = country.phoneFormat || '';
|
|
20
|
+
} else {
|
|
21
|
+
prefix = '';
|
|
22
|
+
suffix = number.slice(1);
|
|
23
|
+
format = '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { prefix, suffix, format };
|
|
27
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { filterOptionsForQuery } from '..';
|
|
2
|
+
|
|
3
|
+
const OPTIONS = [
|
|
4
|
+
{
|
|
5
|
+
name: 'test1',
|
|
6
|
+
iso2: 'TT',
|
|
7
|
+
iso3: 'TT1',
|
|
8
|
+
phone: '+93',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: 'something',
|
|
12
|
+
iso2: 'ST',
|
|
13
|
+
iso3: 'SMT',
|
|
14
|
+
phone: '+33',
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
describe('filterOptionsForQuery', () => {
|
|
19
|
+
it('filters options based on all properties', () => {
|
|
20
|
+
const option1 = OPTIONS[0];
|
|
21
|
+
const option2 = OPTIONS[1];
|
|
22
|
+
|
|
23
|
+
expect(filterOptionsForQuery(OPTIONS, option1.name)).toStrictEqual([option1]);
|
|
24
|
+
expect(filterOptionsForQuery(OPTIONS, option1.iso2)).toStrictEqual([option1]);
|
|
25
|
+
expect(filterOptionsForQuery(OPTIONS, option1.iso3)).toStrictEqual([option1]);
|
|
26
|
+
expect(filterOptionsForQuery(OPTIONS, option1.phone)).toStrictEqual([option1]);
|
|
27
|
+
expect(filterOptionsForQuery(OPTIONS, option2.name)).toStrictEqual([option2]);
|
|
28
|
+
expect(filterOptionsForQuery(OPTIONS, option2.iso2)).toStrictEqual([option2]);
|
|
29
|
+
expect(filterOptionsForQuery(OPTIONS, option2.iso3)).toStrictEqual([option2]);
|
|
30
|
+
expect(filterOptionsForQuery(OPTIONS, option2.phone)).toStrictEqual([option2]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return an emtpy array if option cannot be found', () => {
|
|
34
|
+
expect(filterOptionsForQuery(OPTIONS, 'AA')).toStrictEqual([]);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isOptionAndFitsQuery } from '../isOptionAndFitsQuery';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Filters a set of options based on search string
|
|
5
|
+
*
|
|
6
|
+
* @param options
|
|
7
|
+
* @param query
|
|
8
|
+
* @returns {*}
|
|
9
|
+
*/
|
|
10
|
+
export const filterOptionsForQuery = (options, query) =>
|
|
11
|
+
options.filter((option) => isOptionAndFitsQuery(option, query));
|
|
@@ -14,6 +14,7 @@ describe('findCountryByCode', () => {
|
|
|
14
14
|
|
|
15
15
|
it('should return null for invalid code', () => {
|
|
16
16
|
expect(findCountryByCode('Wrong')).toBeNull();
|
|
17
|
+
expect(findCountryByCode(null)).toBeNull();
|
|
17
18
|
expect(findCountryByCode('')).toBeNull();
|
|
18
19
|
expect(findCountryByCode(' ')).toBeNull();
|
|
19
20
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import countries from '../../data/countries';
|
|
2
|
+
import { longestMatchingPrefix } from '../longestMatchingPrefix';
|
|
3
|
+
|
|
4
|
+
export const findCountryByCode = (code) => {
|
|
5
|
+
let matchingCodes;
|
|
6
|
+
if (code && code.length === 2) {
|
|
7
|
+
matchingCodes = countries.filter((country) => code.toUpperCase() === country.iso2);
|
|
8
|
+
}
|
|
9
|
+
return matchingCodes && matchingCodes.length > 0 ? longestMatchingPrefix(matchingCodes) : null;
|
|
10
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import countries from '../../data/countries';
|
|
2
|
+
import { longestMatchingPrefix } from '../longestMatchingPrefix';
|
|
3
|
+
|
|
4
|
+
export const findCountryByPrefix = (number) => {
|
|
5
|
+
let matchingCodes = null;
|
|
6
|
+
if (number && number.length > 1) {
|
|
7
|
+
matchingCodes = countries.filter((country) => number.indexOf(country.phone) === 0);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return matchingCodes && matchingCodes.length > 0 ? longestMatchingPrefix(matchingCodes) : null;
|
|
11
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isArray } from '@transferwise/neptune-validation';
|
|
2
|
+
|
|
3
|
+
export const groupCountriesByPrefix = (countries) => {
|
|
4
|
+
const groupedArray = countries.reduce((accumulator, country) => {
|
|
5
|
+
const { name, iso2, iso3, phone } = country;
|
|
6
|
+
if (accumulator[phone]) {
|
|
7
|
+
const previousValue = accumulator[phone];
|
|
8
|
+
accumulator[phone] = {
|
|
9
|
+
...previousValue,
|
|
10
|
+
name: isArray(previousValue.name)
|
|
11
|
+
? [...previousValue.name, name]
|
|
12
|
+
: [previousValue.name, name],
|
|
13
|
+
iso2: isArray(previousValue.iso2)
|
|
14
|
+
? [...previousValue.iso2, iso2]
|
|
15
|
+
: [previousValue.iso2, iso2],
|
|
16
|
+
iso3: isArray(previousValue.iso3)
|
|
17
|
+
? [...previousValue.iso3, iso3]
|
|
18
|
+
: [previousValue.iso3, iso3],
|
|
19
|
+
};
|
|
20
|
+
} else {
|
|
21
|
+
accumulator[phone] = country;
|
|
22
|
+
}
|
|
23
|
+
return accumulator;
|
|
24
|
+
}, {});
|
|
25
|
+
return Object.values(groupedArray);
|
|
26
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { groupCountriesByPrefix } from '.';
|
|
2
|
+
|
|
3
|
+
const countries = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Canada',
|
|
6
|
+
iso2: 'CA',
|
|
7
|
+
iso3: 'CAN',
|
|
8
|
+
phone: '+1',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: 'United States of America',
|
|
12
|
+
iso2: 'US',
|
|
13
|
+
iso3: 'USA',
|
|
14
|
+
phone: '+1',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'United States Minor Outlying Islands',
|
|
18
|
+
iso2: 'UM',
|
|
19
|
+
iso3: 'UMI',
|
|
20
|
+
phone: '+1',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'United Kingdom',
|
|
24
|
+
iso2: 'GB',
|
|
25
|
+
iso3: 'GBR',
|
|
26
|
+
phone: '+44',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'Guernsey',
|
|
30
|
+
iso2: 'GG',
|
|
31
|
+
iso3: 'GGY',
|
|
32
|
+
phone: '+44',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Guinea',
|
|
36
|
+
iso2: 'GN',
|
|
37
|
+
iso3: 'GIN',
|
|
38
|
+
phone: '+224',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const groupedCountries = [
|
|
43
|
+
{
|
|
44
|
+
name: ['Canada', 'United States of America', 'United States Minor Outlying Islands'],
|
|
45
|
+
iso2: ['CA', 'US', 'UM'],
|
|
46
|
+
iso3: ['CAN', 'USA', 'UMI'],
|
|
47
|
+
phone: '+1',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: ['United Kingdom', 'Guernsey'],
|
|
51
|
+
iso2: ['GB', 'GG'],
|
|
52
|
+
iso3: ['GBR', 'GGY'],
|
|
53
|
+
phone: '+44',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Guinea',
|
|
57
|
+
iso2: 'GN',
|
|
58
|
+
iso3: 'GIN',
|
|
59
|
+
phone: '+224',
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
describe('groupCountriesByPrefix', () => {
|
|
64
|
+
it('groups countries by prefix', () => {
|
|
65
|
+
expect(groupCountriesByPrefix(countries)).toStrictEqual(groupedCountries);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -4,6 +4,8 @@ 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';
|
|
7
9
|
export { cleanNumber } from './cleanNumber';
|
|
8
10
|
export { isStringNumeric } from './isStringNumeric';
|
|
9
11
|
export { sortArrayByProperty } from './sortArrayByProperty';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { isOptionAndFitsQuery, startsWith } from './isOptionAndFitsQuery';
|