@transferwise/components 0.0.0-experimental-4b7aba1 → 0.0.0-experimental-16e2eec
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 +115 -368
- package/build/index.esm.js.map +1 -1
- package/build/index.js +114 -368
- package/build/index.js.map +1 -1
- package/build/main.css +0 -98
- package/build/styles/main.css +0 -98
- package/build/types/common/locale/index.d.ts +26 -43
- package/build/types/common/locale/index.d.ts.map +1 -1
- package/build/types/index.d.ts +0 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts +22 -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/package.json +1 -1
- package/src/common/locale/{index.spec.js → index.spec.ts} +4 -4
- package/src/common/locale/index.ts +96 -0
- package/src/index.ts +0 -1
- package/src/main.css +0 -98
- package/src/main.less +0 -1
- package/src/phoneNumberInput/PhoneNumberInput.spec.js +18 -22
- package/src/phoneNumberInput/PhoneNumberInput.tsx +193 -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 +24 -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/ssr.spec.js +0 -17
- package/build/styles/segmentedControl/SegmentedControl.css +0 -98
- 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/build/types/segmentedControl/SegmentedControl.d.ts +0 -31
- package/build/types/segmentedControl/SegmentedControl.d.ts.map +0 -1
- package/build/types/segmentedControl/index.d.ts +0 -3
- package/build/types/segmentedControl/index.d.ts.map +0 -1
- package/src/common/locale/index.js +0 -139
- 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/segmentedControl/SegmentedControl.css +0 -98
- package/src/segmentedControl/SegmentedControl.less +0 -99
- package/src/segmentedControl/SegmentedControl.spec.tsx +0 -107
- package/src/segmentedControl/SegmentedControl.story.tsx +0 -55
- package/src/segmentedControl/SegmentedControl.tsx +0 -150
- package/src/segmentedControl/__snapshots__/SegmentedControl.spec.tsx.snap +0 -113
- package/src/segmentedControl/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
|
@@ -1,210 +0,0 @@
|
|
|
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;
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
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));
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,67 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { isOptionAndFitsQuery, startsWith } from './isOptionAndFitsQuery';
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { isArray } from '@transferwise/neptune-validation';
|
|
2
|
-
/**
|
|
3
|
-
* Checks if query is contained into object properties.
|
|
4
|
-
*
|
|
5
|
-
* @param {object} option - the select option
|
|
6
|
-
* @param {string} query - the current search query
|
|
7
|
-
* @returns {boolean}
|
|
8
|
-
*/
|
|
9
|
-
export const isOptionAndFitsQuery = (option, query) =>
|
|
10
|
-
startsWith(option.iso3, query) ||
|
|
11
|
-
startsWith(option.iso2, query) ||
|
|
12
|
-
startsWith(option.name, query) ||
|
|
13
|
-
startsWith(option.phone, query);
|
|
14
|
-
|
|
15
|
-
export const startsWith = (property, query) => {
|
|
16
|
-
if (isArray(property)) {
|
|
17
|
-
return (
|
|
18
|
-
property.filter((proper) => normalizeValue(proper).indexOf(normalizeValue(query)) === 0)
|
|
19
|
-
.length > 0
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
return normalizeValue(property).indexOf(normalizeValue(query)) === 0;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const normalizeValue = (value) => value.toLowerCase().replace('+', '');
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { startsWith, isOptionAndFitsQuery } from '.';
|
|
2
|
-
|
|
3
|
-
const DATA_TEST = [
|
|
4
|
-
{
|
|
5
|
-
name: 'test1',
|
|
6
|
-
iso2: 'TT',
|
|
7
|
-
iso3: 'TT1',
|
|
8
|
-
phone: '+93',
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
name: 'test1',
|
|
12
|
-
iso2: ['TT', 'AA'],
|
|
13
|
-
iso3: 'TT1',
|
|
14
|
-
phone: '+93',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
name: 'something',
|
|
18
|
-
iso2: 'ST',
|
|
19
|
-
iso3: 'SMT',
|
|
20
|
-
phone: '+33',
|
|
21
|
-
},
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
describe('isOptionAndFitsQuery', () => {
|
|
25
|
-
describe('when option is given', () => {
|
|
26
|
-
it('should return true if query is relevant', () => {
|
|
27
|
-
expect(isOptionAndFitsQuery(DATA_TEST[0], 'TT')).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should return true if query is relevant and one of the array values', () => {
|
|
31
|
-
expect(isOptionAndFitsQuery(DATA_TEST[1], 'TT')).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should return false if query is not relevant and not one of the array values', () => {
|
|
35
|
-
expect(isOptionAndFitsQuery(DATA_TEST[1], 'BB')).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should return false if query is not relevant', () => {
|
|
39
|
-
expect(isOptionAndFitsQuery(DATA_TEST[0], 'AA')).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('startWith', () => {
|
|
45
|
-
describe('when property is given', () => {
|
|
46
|
-
it('returns true if any of the values starts with the query', () => {
|
|
47
|
-
expect(startsWith('AA', 'AA')).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it(`returns false if value doesn't start with`, () => {
|
|
51
|
-
expect(startsWith('AABB', 'BB')).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should return true if query is contained in grouped options', () => {
|
|
55
|
-
expect(startsWith(['AA', 'BB'], 'BB')).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("returns false if any value in an array doesn't start with", () => {
|
|
59
|
-
expect(startsWith(['CCAA', 'CCBB'], 'BB')).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('returns false for an empty value', () => {
|
|
63
|
-
expect(startsWith('', 'BB')).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const isStringNumeric = (value) => /^\+?[\d-\s]+$/.test(value);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* @param phoneNumber
|
|
4
|
-
* @returns {boolean} - returns true for number that starts with '+' and contains a mix of digits and spaces with
|
|
5
|
-
* at least 4 digits.
|
|
6
|
-
*/
|
|
7
|
-
export const isValidPhoneNumber = (phoneNumber) =>
|
|
8
|
-
/^\+[\d-\s]+$/.test(phoneNumber) &&
|
|
9
|
-
phoneNumber.match(/\d+/g) &&
|
|
10
|
-
phoneNumber.match(/\d+/g).join('').length >= 4;
|
|
@@ -1,25 +0,0 @@
|
|
|
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 {string} locale - a string that represent the locale ex:'es-ES'
|
|
13
|
-
* @param countryCode
|
|
14
|
-
* @returns {string}
|
|
15
|
-
*/
|
|
16
|
-
export const setDefaultPrefix = (locale, countryCode) => {
|
|
17
|
-
const country =
|
|
18
|
-
findCountryByCode(countryCode) ||
|
|
19
|
-
// when `locale` code has explicit region: `en-GB`, `en-US`, `ar-AE`
|
|
20
|
-
findCountryByCode(getCountryFromLocale(locale)) ||
|
|
21
|
-
// when `locale` code is only two chars value: `fr`, `es`
|
|
22
|
-
findCountryByCode(locale);
|
|
23
|
-
|
|
24
|
-
return country?.phone || DEFAULT_PHONE_CODE;
|
|
25
|
-
};
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
.segmented-control {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
--segment-highlight-width: 0;
|
|
4
|
-
--segment-highlight-x: var(--size-4);
|
|
5
|
-
}
|
|
6
|
-
.segmented-control__segments {
|
|
7
|
-
display: inline-flex;
|
|
8
|
-
position: relative;
|
|
9
|
-
justify-content: center;
|
|
10
|
-
align-items: center;
|
|
11
|
-
background: rgba(134,167,189,0.10196);
|
|
12
|
-
background: var(--color-background-neutral);
|
|
13
|
-
padding: 4px;
|
|
14
|
-
padding: var(--size-4);
|
|
15
|
-
border-radius: 24px;
|
|
16
|
-
border-radius: var(--size-24);
|
|
17
|
-
width: 100%;
|
|
18
|
-
transition: outline 300ms;
|
|
19
|
-
outline: 2px solid transparent;
|
|
20
|
-
}
|
|
21
|
-
.segmented-control--input:has(:focus-visible) > .segmented-control__segments::after {
|
|
22
|
-
outline: 2px solid var(--color-interactive-primary);
|
|
23
|
-
}
|
|
24
|
-
.segmented-control__segments::after {
|
|
25
|
-
content: "";
|
|
26
|
-
background: #ffffff;
|
|
27
|
-
background: var(--color-background-screen);
|
|
28
|
-
width: var(--segment-highlight-width);
|
|
29
|
-
position: absolute;
|
|
30
|
-
top: 4px;
|
|
31
|
-
top: var(--size-4);
|
|
32
|
-
bottom: 4px;
|
|
33
|
-
bottom: var(--size-4);
|
|
34
|
-
left: var(--segment-highlight-x);
|
|
35
|
-
z-index: 0;
|
|
36
|
-
border-radius: 24px;
|
|
37
|
-
border-radius: var(--size-24);
|
|
38
|
-
transition: left 300ms;
|
|
39
|
-
}
|
|
40
|
-
.segmented-control__segments--no-animate::after {
|
|
41
|
-
transition: none !important;
|
|
42
|
-
}
|
|
43
|
-
.segmented-control__segment {
|
|
44
|
-
position: relative;
|
|
45
|
-
flex: 1 1 100%;
|
|
46
|
-
flex-flow: column;
|
|
47
|
-
min-width: 0;
|
|
48
|
-
align-items: center;
|
|
49
|
-
text-align: center;
|
|
50
|
-
vertical-align: middle;
|
|
51
|
-
padding: 8px 16px;
|
|
52
|
-
padding: var(--size-8) var(--size-16);
|
|
53
|
-
margin: 0;
|
|
54
|
-
border-radius: 24px;
|
|
55
|
-
border-radius: var(--size-24);
|
|
56
|
-
z-index: 1;
|
|
57
|
-
cursor: pointer;
|
|
58
|
-
transition: font-weight 300ms, background 300ms;
|
|
59
|
-
}
|
|
60
|
-
.segmented-control__segment:hover {
|
|
61
|
-
background: rgba(0,0,0,0.10196);
|
|
62
|
-
background: var(--color-background-overlay);
|
|
63
|
-
}
|
|
64
|
-
.segmented-control__radio-input {
|
|
65
|
-
position: fixed;
|
|
66
|
-
opacity: 0;
|
|
67
|
-
pointer-events: none;
|
|
68
|
-
}
|
|
69
|
-
.segmented-control__button {
|
|
70
|
-
background: none;
|
|
71
|
-
-webkit-appearance: none;
|
|
72
|
-
-moz-appearance: none;
|
|
73
|
-
appearance: none;
|
|
74
|
-
border: none;
|
|
75
|
-
outline: none;
|
|
76
|
-
width: 100%;
|
|
77
|
-
height: 100%;
|
|
78
|
-
font: inherit;
|
|
79
|
-
padding: none;
|
|
80
|
-
outline: 2px solid transparent;
|
|
81
|
-
}
|
|
82
|
-
.segmented-control__button:focus {
|
|
83
|
-
outline-offset: 0px;
|
|
84
|
-
}
|
|
85
|
-
.segmented-control__button:focus-visible {
|
|
86
|
-
outline-color: var(--color-interactive-primary);
|
|
87
|
-
}
|
|
88
|
-
.segmented-control__selected-segment:hover {
|
|
89
|
-
background: transparent;
|
|
90
|
-
}
|
|
91
|
-
.segmented-control__selected-segment {
|
|
92
|
-
font-weight: 800;
|
|
93
|
-
font-weight: var(--font-weight-bold);
|
|
94
|
-
}
|
|
95
|
-
.segmented-control__label-text {
|
|
96
|
-
word-wrap: break-word;
|
|
97
|
-
color: var(--color-interactive-primary);
|
|
98
|
-
}
|