@transferwise/components 0.0.0-experimental-2e23c2a → 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 +185 -343
- package/build/index.esm.js.map +1 -1
- package/build/index.js +184 -342
- package/build/index.js.map +1 -1
- package/build/main.css +1 -3
- package/build/styles/inputs/SelectInput.css +1 -3
- package/build/styles/main.css +1 -3
- package/build/types/common/locale/index.d.ts +26 -43
- package/build/types/common/locale/index.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts +5 -6
- package/build/types/inputs/SelectInput.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/build/types/utilities/wrapInFragment.d.ts +3 -0
- package/build/types/utilities/wrapInFragment.d.ts.map +1 -0
- 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/inputs/SelectInput.css +1 -3
- package/src/inputs/SelectInput.less +1 -4
- package/src/inputs/SelectInput.spec.tsx +0 -26
- package/src/inputs/SelectInput.story.tsx +1 -73
- package/src/inputs/SelectInput.tsx +85 -104
- package/src/main.css +1 -3
- 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/utilities/wrapInFragment.tsx +3 -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/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/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,96 @@
|
|
|
1
|
+
import { Direction } from '..';
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_LANG = 'en';
|
|
4
|
+
export const DEFAULT_LOCALE = 'en-GB';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Languages written right-to-left.
|
|
8
|
+
*/
|
|
9
|
+
export const RTL_LANGUAGES = ['ar', 'he'];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated The source of truth for supported languages lives in Crab.
|
|
13
|
+
*/
|
|
14
|
+
export const SUPPORTED_LANGUAGES = [
|
|
15
|
+
DEFAULT_LANG,
|
|
16
|
+
'de',
|
|
17
|
+
'es',
|
|
18
|
+
'fr',
|
|
19
|
+
'hu',
|
|
20
|
+
'id',
|
|
21
|
+
'it',
|
|
22
|
+
'ja',
|
|
23
|
+
'pl',
|
|
24
|
+
'pt',
|
|
25
|
+
'ro',
|
|
26
|
+
'ru',
|
|
27
|
+
'th',
|
|
28
|
+
'tr',
|
|
29
|
+
'uk',
|
|
30
|
+
'zh',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Verifies and adjusts locale, replacing `_` with `-`.
|
|
35
|
+
*
|
|
36
|
+
* @param locale `es`, `es_ES`, `en-GB`, `en`, `ja`, `ja-JP`, etc.
|
|
37
|
+
* @returns `null` if locale is unrecognized by `Intl.Locale`.
|
|
38
|
+
*/
|
|
39
|
+
export function adjustLocale(locale: string) {
|
|
40
|
+
const localeTrimmed = locale?.trim();
|
|
41
|
+
if (localeTrimmed) {
|
|
42
|
+
try {
|
|
43
|
+
return new Intl.Locale(localeTrimmed.replace('_', '-')).baseName;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.error(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Provides corresponding lang (iso2) for provided locale.
|
|
54
|
+
*
|
|
55
|
+
* @deprecated The use of this function almost always breaks language variants
|
|
56
|
+
* e.g. Simplified and Traditional Chinese.
|
|
57
|
+
* There should be no use case for this function.
|
|
58
|
+
* To select the correct translations from a translations object, pass the
|
|
59
|
+
* locale directly into Crab's getLocalisedMessages.
|
|
60
|
+
* @param locale `es`, `es-ES`, `en-GB`, `en`, `ja`, `ja-JP`, etc.
|
|
61
|
+
* @returns Two-letter ISO 639-1 language code, falling back to `null` if locale is invalid or language is unsupported.
|
|
62
|
+
*/
|
|
63
|
+
export function getLangFromLocale(locale: string) {
|
|
64
|
+
const adjustedLocale = adjustLocale(locale);
|
|
65
|
+
if (adjustedLocale != null) {
|
|
66
|
+
const { language } = new Intl.Locale(adjustedLocale);
|
|
67
|
+
if (SUPPORTED_LANGUAGES.includes(language)) {
|
|
68
|
+
return language;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Provides corresponding country code (iso2) for locales code with explicit region value.
|
|
76
|
+
*
|
|
77
|
+
* @param locale `es-ES`, `en-GB`, `ja-JP`, etc.
|
|
78
|
+
* @returns `null` if the locale is invalid or the region can‘t be identified.
|
|
79
|
+
*/
|
|
80
|
+
export function getCountryFromLocale(locale: string) {
|
|
81
|
+
const adjustedLocale = adjustLocale(locale);
|
|
82
|
+
return adjustedLocale != null ? new Intl.Locale(adjustedLocale).region ?? null : null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Provides the layout direction for a given locale.
|
|
87
|
+
*
|
|
88
|
+
* @param locale `es`, `es-ES`, `en-GB`, `en`, `ja`, `ja-JP`, etc.
|
|
89
|
+
* @returns The layout direction based on the locale, falling back to `Direction.LTR` if the locale is invalid or unsupported.
|
|
90
|
+
*/
|
|
91
|
+
export function getDirectionFromLocale(locale: string) {
|
|
92
|
+
const adjustedLocale = adjustLocale(locale);
|
|
93
|
+
return adjustedLocale != null && RTL_LANGUAGES.includes(new Intl.Locale(adjustedLocale).language)
|
|
94
|
+
? Direction.RTL
|
|
95
|
+
: Direction.LTR;
|
|
96
|
+
}
|
|
@@ -158,12 +158,10 @@
|
|
|
158
158
|
color: #5d7079;
|
|
159
159
|
color: var(--color-content-secondary);
|
|
160
160
|
}
|
|
161
|
-
.np-select-input-
|
|
161
|
+
.np-select-input-placeholder {
|
|
162
162
|
overflow: hidden;
|
|
163
163
|
text-overflow: ellipsis;
|
|
164
164
|
white-space: nowrap;
|
|
165
|
-
}
|
|
166
|
-
.np-select-input-placeholder {
|
|
167
165
|
color: #768e9c;
|
|
168
166
|
color: var(--color-content-tertiary);
|
|
169
167
|
}
|
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
@import "./_Popover.less";
|
|
4
4
|
@import (reference) "../../node_modules/@transferwise/neptune-css/src/less/ring.less";
|
|
5
5
|
|
|
6
|
-
.np-select-input-
|
|
6
|
+
.np-select-input-placeholder {
|
|
7
7
|
overflow: hidden;
|
|
8
8
|
text-overflow: ellipsis;
|
|
9
9
|
white-space: nowrap;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.np-select-input-placeholder {
|
|
13
10
|
color: var(--color-content-tertiary);
|
|
14
11
|
}
|
|
15
12
|
|
|
@@ -182,30 +182,4 @@ describe('SelectInput', () => {
|
|
|
182
182
|
const listbox = screen.getByRole('listbox');
|
|
183
183
|
expect(within(listbox).getAllByRole('option')).toHaveLength(2);
|
|
184
184
|
});
|
|
185
|
-
|
|
186
|
-
it('selects multiple options', async () => {
|
|
187
|
-
render(
|
|
188
|
-
<SelectInput
|
|
189
|
-
multiple
|
|
190
|
-
items={[
|
|
191
|
-
{ type: 'option', value: 'USD' },
|
|
192
|
-
{ type: 'option', value: 'EUR' },
|
|
193
|
-
]}
|
|
194
|
-
/>,
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
const trigger = screen.getAllByRole('button')[0];
|
|
198
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
199
|
-
await act(async () => {
|
|
200
|
-
userEvent.click(trigger);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const listbox = screen.getByRole('listbox');
|
|
204
|
-
const options = within(listbox).getAllByRole('option');
|
|
205
|
-
options.forEach((option) => {
|
|
206
|
-
userEvent.click(option);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
expect(trigger).toHaveTextContent('USD, EUR');
|
|
210
|
-
});
|
|
211
185
|
});
|
|
@@ -242,7 +242,7 @@ export const Currencies: StoryObj<{
|
|
|
242
242
|
onChange: (value: Currency) => void;
|
|
243
243
|
}> = {
|
|
244
244
|
render: function Story({ onChange }) {
|
|
245
|
-
const [selectedCurrency, setSelectedCurrency] = useState(popularCurrencies[0]);
|
|
245
|
+
const [selectedCurrency, setSelectedCurrency] = useState<Currency>(popularCurrencies[0]);
|
|
246
246
|
|
|
247
247
|
return (
|
|
248
248
|
<SelectInput
|
|
@@ -301,78 +301,6 @@ export const Currencies: StoryObj<{
|
|
|
301
301
|
},
|
|
302
302
|
};
|
|
303
303
|
|
|
304
|
-
export const MultipleCurrencies: StoryObj<{
|
|
305
|
-
onChange: (value: Currency[]) => void;
|
|
306
|
-
}> = {
|
|
307
|
-
render: function Story({ onChange }) {
|
|
308
|
-
const [selectedCurrencies, setSelectedCurrencies] = useState<readonly Currency[]>([
|
|
309
|
-
popularCurrencies[0],
|
|
310
|
-
]);
|
|
311
|
-
|
|
312
|
-
return (
|
|
313
|
-
<SelectInput
|
|
314
|
-
multiple
|
|
315
|
-
placeholder="Choose currencies…"
|
|
316
|
-
items={[
|
|
317
|
-
{
|
|
318
|
-
type: 'group',
|
|
319
|
-
label: 'Popular currencies',
|
|
320
|
-
options: popularCurrencies.map((currency) => currencyOption(currency)),
|
|
321
|
-
},
|
|
322
|
-
{
|
|
323
|
-
type: 'group',
|
|
324
|
-
label: 'All currencies',
|
|
325
|
-
options: allCurrencies.map((currency) => currencyOption(currency)),
|
|
326
|
-
},
|
|
327
|
-
]}
|
|
328
|
-
value={selectedCurrencies}
|
|
329
|
-
renderValue={(currency, withinTrigger) =>
|
|
330
|
-
withinTrigger ? (
|
|
331
|
-
currency.code
|
|
332
|
-
) : (
|
|
333
|
-
<SelectInputOptionContent
|
|
334
|
-
title={currency.code}
|
|
335
|
-
note={currency.name}
|
|
336
|
-
icon={<Flag code={currency.code} intrinsicSize={24} />}
|
|
337
|
-
/>
|
|
338
|
-
)
|
|
339
|
-
}
|
|
340
|
-
// eslint-disable-next-line sonarjs/no-identical-functions
|
|
341
|
-
renderFooter={({ resultsEmpty, queryNormalized: normalizedQuery }) =>
|
|
342
|
-
resultsEmpty && normalizedQuery != null && /^[a-z]{3}$/u.test(normalizedQuery) ? (
|
|
343
|
-
<>
|
|
344
|
-
It’s not possible use {normalizedQuery.toUpperCase()} yet.{' '}
|
|
345
|
-
<a href="#_" className="np-text-link-default">
|
|
346
|
-
Email me when it’s available.
|
|
347
|
-
</a>
|
|
348
|
-
</>
|
|
349
|
-
) : (
|
|
350
|
-
<>
|
|
351
|
-
Can’t find it?{' '}
|
|
352
|
-
<a href="#_" className="np-text-link-default">
|
|
353
|
-
Request the currency you need,
|
|
354
|
-
</a>{' '}
|
|
355
|
-
and we’ll notify you once it’s available.
|
|
356
|
-
</>
|
|
357
|
-
)
|
|
358
|
-
}
|
|
359
|
-
filterable
|
|
360
|
-
filterPlaceholder="Type a currency / country"
|
|
361
|
-
size="lg"
|
|
362
|
-
onChange={(currency) => {
|
|
363
|
-
setSelectedCurrencies(currency);
|
|
364
|
-
onChange(currency);
|
|
365
|
-
}}
|
|
366
|
-
/>
|
|
367
|
-
);
|
|
368
|
-
},
|
|
369
|
-
argTypes: {
|
|
370
|
-
onChange: {
|
|
371
|
-
action: 'changed',
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
};
|
|
375
|
-
|
|
376
304
|
export const CustomTrigger: StoryObj = {
|
|
377
305
|
render: function Story() {
|
|
378
306
|
return (
|
|
@@ -12,6 +12,7 @@ import { useScreenSize } from '../common/hooks/useScreenSize';
|
|
|
12
12
|
import { PolymorphicWithOverrides } from '../common/polymorphicWithOverrides/PolymorphicWithOverrides';
|
|
13
13
|
import { Breakpoint } from '../common/propsValues/breakpoint';
|
|
14
14
|
import dateTriggerMessages from '../dateLookup/dateTrigger/DateTrigger.messages';
|
|
15
|
+
import { wrapInFragment } from '../utilities/wrapInFragment';
|
|
15
16
|
import { Merge } from '../utils';
|
|
16
17
|
|
|
17
18
|
import { InputGroup } from './InputGroup';
|
|
@@ -124,13 +125,13 @@ function filterSelectInputItems<T>(items: readonly SelectInputItem<T>[], needle:
|
|
|
124
125
|
});
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
export interface SelectInputProps<T = string
|
|
128
|
+
export interface SelectInputProps<T = string> {
|
|
128
129
|
name?: string;
|
|
129
|
-
multiple?: M;
|
|
130
130
|
placeholder?: string;
|
|
131
|
+
// TODO: multiple?: boolean;
|
|
131
132
|
items: readonly SelectInputItem<NonNullable<T>>[];
|
|
132
|
-
defaultValue?:
|
|
133
|
-
value?:
|
|
133
|
+
defaultValue?: T;
|
|
134
|
+
value?: T;
|
|
134
135
|
compareValues?:
|
|
135
136
|
| (keyof NonNullable<T> & string)
|
|
136
137
|
| ((a: T | undefined, b: T | undefined) => boolean);
|
|
@@ -153,7 +154,7 @@ export interface SelectInputProps<T = string, M extends boolean = false> {
|
|
|
153
154
|
size?: 'sm' | 'md' | 'lg';
|
|
154
155
|
className?: string;
|
|
155
156
|
onFilterChange?: (args: { query: string; queryNormalized: string | null }) => void;
|
|
156
|
-
onChange?: (value:
|
|
157
|
+
onChange?: (value: T) => void;
|
|
157
158
|
onClear?: () => void;
|
|
158
159
|
}
|
|
159
160
|
|
|
@@ -185,14 +186,7 @@ const defaultRenderTrigger = (({ content, placeholderShown, clear, disabled, siz
|
|
|
185
186
|
className={className}
|
|
186
187
|
>
|
|
187
188
|
<SelectInputTriggerButton as={ButtonInput} size={size}>
|
|
188
|
-
<span
|
|
189
|
-
className={classNames(
|
|
190
|
-
'np-select-input-content',
|
|
191
|
-
placeholderShown && 'np-select-input-placeholder',
|
|
192
|
-
)}
|
|
193
|
-
>
|
|
194
|
-
{content}
|
|
195
|
-
</span>
|
|
189
|
+
{placeholderShown ? <span className="np-select-input-placeholder"> {content}</span> : content}
|
|
196
190
|
</SelectInputTriggerButton>
|
|
197
191
|
</InputGroup>
|
|
198
192
|
)) satisfies SelectInputProps['renderTrigger'];
|
|
@@ -217,15 +211,14 @@ function SelectInputClearButton({ className, onClick }: SelectInputClearButtonPr
|
|
|
217
211
|
|
|
218
212
|
const noop = () => {};
|
|
219
213
|
|
|
220
|
-
export function SelectInput<T = string
|
|
214
|
+
export function SelectInput<T = string>({
|
|
221
215
|
name,
|
|
222
|
-
multiple = false as M,
|
|
223
216
|
placeholder,
|
|
224
217
|
items,
|
|
225
218
|
defaultValue,
|
|
226
219
|
value: controlledValue,
|
|
227
220
|
compareValues,
|
|
228
|
-
renderValue =
|
|
221
|
+
renderValue = wrapInFragment,
|
|
229
222
|
renderFooter,
|
|
230
223
|
renderTrigger = defaultRenderTrigger,
|
|
231
224
|
filterable,
|
|
@@ -236,7 +229,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
236
229
|
onFilterChange = noop,
|
|
237
230
|
onChange,
|
|
238
231
|
onClear,
|
|
239
|
-
}: SelectInputProps<T
|
|
232
|
+
}: SelectInputProps<T>) {
|
|
240
233
|
const [open, setOpen] = useState(false);
|
|
241
234
|
|
|
242
235
|
const [filterQuery, _setFilterQuery] = useState('');
|
|
@@ -260,106 +253,94 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
260
253
|
return (
|
|
261
254
|
<ListboxBase
|
|
262
255
|
name={name}
|
|
263
|
-
multiple={multiple}
|
|
264
256
|
defaultValue={defaultValue}
|
|
265
257
|
value={controlledValue}
|
|
266
258
|
// TODO: Remove assertion when upgrading TypeScript to v5
|
|
267
259
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
268
260
|
by={compareValues as any}
|
|
269
261
|
disabled={disabled}
|
|
270
|
-
onChange={
|
|
271
|
-
(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
onChange?.(value);
|
|
276
|
-
}) satisfies SelectInputProps<T, M>['onChange']
|
|
277
|
-
}
|
|
262
|
+
onChange={(value) => {
|
|
263
|
+
setOpen(false);
|
|
264
|
+
onChange?.(value);
|
|
265
|
+
}}
|
|
278
266
|
>
|
|
279
|
-
{({ disabled: uiDisabled, value }) =>
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
267
|
+
{({ disabled: uiDisabled, value }) => (
|
|
268
|
+
<OptionsOverlay
|
|
269
|
+
placement="bottom-start"
|
|
270
|
+
open={open}
|
|
271
|
+
renderTrigger={({ ref, getInteractionProps }) => (
|
|
272
|
+
<SelectInputTriggerButtonPropsContext.Provider
|
|
273
|
+
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
|
274
|
+
value={{
|
|
275
|
+
ref: mergeRefs([ref, triggerRef]),
|
|
276
|
+
...mergeProps(
|
|
277
|
+
{
|
|
278
|
+
onClick: () => {
|
|
279
|
+
setOpen((prev) => !prev);
|
|
280
|
+
},
|
|
281
|
+
onKeyDown: (event: React.KeyboardEvent) => {
|
|
282
|
+
if (
|
|
283
|
+
event.key === ' ' ||
|
|
284
|
+
event.key === 'Enter' ||
|
|
285
|
+
event.key === 'ArrowDown' ||
|
|
286
|
+
event.key === 'ArrowUp'
|
|
287
|
+
) {
|
|
294
288
|
setOpen((prev) => !prev);
|
|
295
|
-
}
|
|
296
|
-
onKeyDown: (event: React.KeyboardEvent) => {
|
|
297
|
-
if (
|
|
298
|
-
event.key === ' ' ||
|
|
299
|
-
event.key === 'Enter' ||
|
|
300
|
-
event.key === 'ArrowDown' ||
|
|
301
|
-
event.key === 'ArrowUp'
|
|
302
|
-
) {
|
|
303
|
-
setOpen((prev) => !prev);
|
|
304
|
-
}
|
|
305
|
-
},
|
|
289
|
+
}
|
|
306
290
|
},
|
|
307
|
-
|
|
308
|
-
),
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
291
|
+
},
|
|
292
|
+
getInteractionProps(),
|
|
293
|
+
),
|
|
294
|
+
}}
|
|
295
|
+
>
|
|
296
|
+
{renderTrigger({
|
|
297
|
+
content:
|
|
298
|
+
value != null ? (
|
|
313
299
|
<SelectInputOptionContentWithinTriggerContext.Provider value>
|
|
314
|
-
{
|
|
315
|
-
? value
|
|
316
|
-
.map((option: NonNullable<T>) => renderValue(option, true))
|
|
317
|
-
.join(', ')
|
|
318
|
-
: renderValue(value as NonNullable<T>, true)}
|
|
300
|
+
{renderValue(value, true)}
|
|
319
301
|
</SelectInputOptionContentWithinTriggerContext.Provider>
|
|
320
302
|
) : (
|
|
321
303
|
placeholder
|
|
322
304
|
),
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}}
|
|
305
|
+
placeholderShown: value == null,
|
|
306
|
+
clear:
|
|
307
|
+
onClear != null
|
|
308
|
+
? () => {
|
|
309
|
+
onClear();
|
|
310
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
311
|
+
}
|
|
312
|
+
: undefined,
|
|
313
|
+
disabled: uiDisabled,
|
|
314
|
+
size,
|
|
315
|
+
className,
|
|
316
|
+
})}
|
|
317
|
+
</SelectInputTriggerButtonPropsContext.Provider>
|
|
318
|
+
)}
|
|
319
|
+
initialFocusRef={controllerRef}
|
|
320
|
+
size={filterable ? 'lg' : 'md'}
|
|
321
|
+
padding="none"
|
|
322
|
+
onClose={() => {
|
|
323
|
+
setOpen(false);
|
|
324
|
+
}}
|
|
325
|
+
onCloseEnd={() => {
|
|
326
|
+
if (filterQuery !== '') {
|
|
327
|
+
setFilterQuery('');
|
|
328
|
+
}
|
|
329
|
+
}}
|
|
330
|
+
>
|
|
331
|
+
<SelectInputOptions
|
|
332
|
+
items={items}
|
|
333
|
+
renderValue={renderValue}
|
|
334
|
+
renderFooter={renderFooter}
|
|
335
|
+
filterable={filterable}
|
|
336
|
+
filterPlaceholder={filterPlaceholder}
|
|
337
|
+
searchInputRef={searchInputRef}
|
|
338
|
+
listboxRef={listboxRef}
|
|
339
|
+
filterQuery={filterQuery}
|
|
340
|
+
onFilterChange={setFilterQuery}
|
|
341
|
+
/>
|
|
342
|
+
</OptionsOverlay>
|
|
343
|
+
)}
|
|
363
344
|
</ListboxBase>
|
|
364
345
|
);
|
|
365
346
|
}
|
|
@@ -449,7 +430,7 @@ interface SelectInputOptionsProps<T = string>
|
|
|
449
430
|
|
|
450
431
|
function SelectInputOptions<T = string>({
|
|
451
432
|
items,
|
|
452
|
-
renderValue =
|
|
433
|
+
renderValue = wrapInFragment,
|
|
453
434
|
renderFooter,
|
|
454
435
|
filterable = false,
|
|
455
436
|
filterPlaceholder,
|
package/src/main.css
CHANGED
|
@@ -2511,12 +2511,10 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
2511
2511
|
color: #5d7079;
|
|
2512
2512
|
color: var(--color-content-secondary);
|
|
2513
2513
|
}
|
|
2514
|
-
.np-select-input-
|
|
2514
|
+
.np-select-input-placeholder {
|
|
2515
2515
|
overflow: hidden;
|
|
2516
2516
|
text-overflow: ellipsis;
|
|
2517
2517
|
white-space: nowrap;
|
|
2518
|
-
}
|
|
2519
|
-
.np-select-input-placeholder {
|
|
2520
2518
|
color: #768e9c;
|
|
2521
2519
|
color: var(--color-content-tertiary);
|
|
2522
2520
|
}
|
|
@@ -37,11 +37,7 @@ 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).
|
|
41
|
-
value: '+44',
|
|
42
|
-
note: 'GBR, GGY, IMN, JEY',
|
|
43
|
-
label: '+44',
|
|
44
|
-
});
|
|
40
|
+
expect(select.props().value).toBe('+44');
|
|
45
41
|
});
|
|
46
42
|
|
|
47
43
|
it('should set number control to empty', () => {
|
|
@@ -49,11 +45,11 @@ describe('Given a telephone number component', () => {
|
|
|
49
45
|
});
|
|
50
46
|
|
|
51
47
|
it('should not disable the select', () => {
|
|
52
|
-
expect(select.prop('disabled')).
|
|
48
|
+
expect(select.prop('disabled')).toBeFalsy();
|
|
53
49
|
});
|
|
54
50
|
|
|
55
51
|
it('should not disable the input', () => {
|
|
56
|
-
expect(input.prop('disabled')).
|
|
52
|
+
expect(input.prop('disabled')).toBeFalsy();
|
|
57
53
|
});
|
|
58
54
|
});
|
|
59
55
|
|
|
@@ -65,7 +61,7 @@ describe('Given a telephone number component', () => {
|
|
|
65
61
|
});
|
|
66
62
|
|
|
67
63
|
it('should set control values correctly', () => {
|
|
68
|
-
expect(select.props().value
|
|
64
|
+
expect(select.props().value).toBe('+39');
|
|
69
65
|
expect(input.prop('value')).toBe('123456789');
|
|
70
66
|
});
|
|
71
67
|
});
|
|
@@ -92,8 +88,8 @@ describe('Given a telephone number component', () => {
|
|
|
92
88
|
input = component.find(NUMBER_SELECTOR);
|
|
93
89
|
});
|
|
94
90
|
|
|
95
|
-
it('should render input with
|
|
96
|
-
expect(input.prop('id')).
|
|
91
|
+
it('should render input with unspecified id', () => {
|
|
92
|
+
expect(input.prop('id')).toBeUndefined();
|
|
97
93
|
});
|
|
98
94
|
});
|
|
99
95
|
|
|
@@ -118,7 +114,7 @@ describe('Given a telephone number component', () => {
|
|
|
118
114
|
it(`${number} code should update the value properly`, () => {
|
|
119
115
|
simulatePaste(component.find('input'), number);
|
|
120
116
|
|
|
121
|
-
expect(select().props().value
|
|
117
|
+
expect(select().props().value).toBe(countryCode);
|
|
122
118
|
expect(input().prop('value')).toBe(localNumber);
|
|
123
119
|
expect(props.onChange).toHaveBeenCalledWith(number.replace(/(\s|-)+/g, ''), countryCode);
|
|
124
120
|
});
|
|
@@ -126,28 +122,28 @@ describe('Given a telephone number component', () => {
|
|
|
126
122
|
|
|
127
123
|
it('should not paste invalid characters', () => {
|
|
128
124
|
simulatePaste(component.find('input'), '+36asdasdasd');
|
|
129
|
-
expect(select().props().value
|
|
125
|
+
expect(select().props().value).toBe('+39');
|
|
130
126
|
expect(input().prop('value')).toBe('123456789');
|
|
131
127
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
132
128
|
});
|
|
133
129
|
|
|
134
130
|
it('should not paste countries which are not in the select', () => {
|
|
135
131
|
simulatePaste(component.find('input'), '+9992342343423');
|
|
136
|
-
expect(select().props().value
|
|
132
|
+
expect(select().props().value).toBe('+39');
|
|
137
133
|
expect(input().prop('value')).toBe('123456789');
|
|
138
134
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
139
135
|
});
|
|
140
136
|
|
|
141
137
|
it("should not paste numbers which doesn't start with the country code", () => {
|
|
142
138
|
simulatePaste(component.find('input'), '0+36303932551');
|
|
143
|
-
expect(select().props().value
|
|
139
|
+
expect(select().props().value).toBe('+39');
|
|
144
140
|
expect(input().prop('value')).toBe('123456789');
|
|
145
141
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
146
142
|
});
|
|
147
143
|
|
|
148
144
|
it("should not paste numbers which doesn't contain a country code", () => {
|
|
149
145
|
simulatePaste(component.find('input'), '06303932551');
|
|
150
|
-
expect(select().props().value
|
|
146
|
+
expect(select().props().value).toBe('+39');
|
|
151
147
|
expect(input().prop('value')).toBe('123456789');
|
|
152
148
|
expect(props.onChange).not.toHaveBeenCalled();
|
|
153
149
|
});
|
|
@@ -161,7 +157,7 @@ describe('Given a telephone number component', () => {
|
|
|
161
157
|
});
|
|
162
158
|
|
|
163
159
|
it('should set the select to the longest matching prefix', () => {
|
|
164
|
-
expect(select.props().value
|
|
160
|
+
expect(select.props().value).toBe('+1868');
|
|
165
161
|
});
|
|
166
162
|
|
|
167
163
|
it('should set the number input to the rest of the number', () => {
|
|
@@ -177,7 +173,7 @@ describe('Given a telephone number component', () => {
|
|
|
177
173
|
});
|
|
178
174
|
|
|
179
175
|
it('should empty the select', () => {
|
|
180
|
-
expect(select.props().value).
|
|
176
|
+
expect(select.props().value).toBeNull();
|
|
181
177
|
});
|
|
182
178
|
|
|
183
179
|
it('should put the whole value in the input without the plus', () => {
|
|
@@ -191,7 +187,7 @@ describe('Given a telephone number component', () => {
|
|
|
191
187
|
select = component.find(PREFIX_SELECT_SELECTOR);
|
|
192
188
|
input = component.find(NUMBER_SELECTOR);
|
|
193
189
|
|
|
194
|
-
expect(select.props().value
|
|
190
|
+
expect(select.props().value).toBe('+44');
|
|
195
191
|
expect(input.prop('value')).toBe('');
|
|
196
192
|
});
|
|
197
193
|
});
|
|
@@ -254,7 +250,7 @@ describe('Given a telephone number component', () => {
|
|
|
254
250
|
});
|
|
255
251
|
|
|
256
252
|
it('should use the prefix of the supplied value', () => {
|
|
257
|
-
expect(select.props().value
|
|
253
|
+
expect(select.props().value).toBe('+1');
|
|
258
254
|
});
|
|
259
255
|
});
|
|
260
256
|
|
|
@@ -268,7 +264,7 @@ describe('Given a telephone number component', () => {
|
|
|
268
264
|
});
|
|
269
265
|
|
|
270
266
|
it('should default the prefix to the local country', () => {
|
|
271
|
-
expect(select.props().value
|
|
267
|
+
expect(select.props().value).toBe('+34');
|
|
272
268
|
});
|
|
273
269
|
});
|
|
274
270
|
|
|
@@ -281,7 +277,7 @@ describe('Given a telephone number component', () => {
|
|
|
281
277
|
});
|
|
282
278
|
|
|
283
279
|
it('should override locale prefix with country specific prefix', () => {
|
|
284
|
-
expect(select.props().value
|
|
280
|
+
expect(select.props().value).toBe('+1');
|
|
285
281
|
});
|
|
286
282
|
});
|
|
287
283
|
});
|
|
@@ -349,7 +345,7 @@ describe('Given a telephone number component', () => {
|
|
|
349
345
|
|
|
350
346
|
it('renders Select component with expected props', () => {
|
|
351
347
|
const select = component.find(PREFIX_SELECT_SELECTOR);
|
|
352
|
-
expect(select.prop('className')).
|
|
348
|
+
expect(select.prop('className')).toBe('custom-class');
|
|
353
349
|
});
|
|
354
350
|
});
|
|
355
351
|
});
|