@transferwise/components 0.0.0-experimental-414f25f → 0.0.0-experimental-ec79c77
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 +59 -118
- package/build/index.esm.js.map +1 -1
- package/build/index.js +59 -118
- package/build/index.js.map +1 -1
- package/build/main.css +0 -107
- package/build/styles/main.css +0 -107
- package/build/types/common/responsivePanel/ResponsivePanel.d.ts.map +1 -1
- package/build/types/dimmer/Dimmer.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts +2 -2
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/inputs/_BottomSheet.d.ts.map +1 -1
- package/build/types/inputs/_Popover.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.d.ts +45 -31
- package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.messages.d.ts +6 -6
- package/build/types/moneyInput/MoneyInput.messages.d.ts.map +1 -1
- package/build/types/moneyInput/currencyFormatting.d.ts +2 -2
- package/build/types/moneyInput/currencyFormatting.d.ts.map +1 -1
- package/build/types/moneyInput/index.d.ts +2 -1
- package/build/types/moneyInput/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/bottomSheet/__snapshots__/BottomSheet.spec.tsx.snap +1 -1
- package/src/common/responsivePanel/ResponsivePanel.tsx +1 -2
- package/src/dimmer/Dimmer.tsx +5 -1
- package/src/index.ts +1 -0
- package/src/inputs/SelectInput.tsx +2 -2
- package/src/inputs/_BottomSheet.tsx +5 -1
- package/src/inputs/_Popover.tsx +5 -1
- package/src/main.css +0 -107
- package/src/main.less +0 -1
- package/src/moneyInput/{MoneyInput.rtl.spec.js → MoneyInput.rtl.spec.tsx} +4 -4
- package/src/moneyInput/MoneyInput.spec.js +109 -49
- package/src/moneyInput/MoneyInput.story.tsx +6 -14
- package/src/moneyInput/{MoneyInput.js → MoneyInput.tsx} +123 -127
- package/src/moneyInput/{currencyFormatting.spec.js → currencyFormatting.spec.ts} +2 -2
- package/src/moneyInput/{currencyFormatting.js → currencyFormatting.ts} +7 -10
- package/src/moneyInput/index.ts +2 -0
- package/src/popover/__snapshots__/Popover.spec.js.snap +1 -1
- package/build/styles/segmentedControl/SegmentedControl.css +0 -107
- package/build/types/segmentedControl/SegmentedControl.d.ts +0 -31
- package/build/types/segmentedControl/SegmentedControl.d.ts.map +0 -1
- package/src/moneyInput/index.js +0 -1
- package/src/segmentedControl/SegmentedControl.css +0 -107
- package/src/segmentedControl/SegmentedControl.less +0 -107
- package/src/segmentedControl/SegmentedControl.story.tsx +0 -55
- package/src/segmentedControl/SegmentedControl.tsx +0 -146
- /package/src/moneyInput/{MoneyInput.messages.js → MoneyInput.messages.ts} +0 -0
|
@@ -1,48 +1,64 @@
|
|
|
1
1
|
import { isEmpty, isNumber, isNull } from '@transferwise/neptune-validation';
|
|
2
2
|
import { Flag } from '@wise/art';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import PropTypes from 'prop-types';
|
|
5
4
|
import { Component } from 'react';
|
|
6
|
-
import { injectIntl } from 'react-intl';
|
|
5
|
+
import { injectIntl, WrappedComponentProps } from 'react-intl';
|
|
7
6
|
|
|
8
7
|
import { Typography } from '../common';
|
|
9
8
|
import { Key as keyValues } from '../common/key';
|
|
10
9
|
import keyCodes from '../common/keyCodes';
|
|
11
|
-
import { Size } from '../common/propsValues/size';
|
|
10
|
+
import { Size, SizeLarge, SizeMedium, SizeSmall } from '../common/propsValues/size';
|
|
12
11
|
import { Input } from '../inputs/Input';
|
|
13
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
SelectInput,
|
|
14
|
+
SelectInputItem,
|
|
15
|
+
SelectInputOptionContent,
|
|
16
|
+
SelectInputOptionItem,
|
|
17
|
+
SelectInputProps,
|
|
18
|
+
} from '../inputs/SelectInput';
|
|
14
19
|
import Title from '../title';
|
|
15
20
|
|
|
16
21
|
import messages from './MoneyInput.messages';
|
|
17
22
|
import { formatAmount, parseAmount } from './currencyFormatting';
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
export interface CurrencyOption {
|
|
25
|
+
value: string;
|
|
26
|
+
label: string;
|
|
27
|
+
currency: string;
|
|
28
|
+
note?: string;
|
|
29
|
+
searchable?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type Currency = { header: string } | ({ header?: never } & CurrencyOption);
|
|
27
33
|
|
|
28
|
-
const isNumberOrNull = (v) => isNumber(v) || isNull(v);
|
|
34
|
+
const isNumberOrNull = (v: unknown): v is number | null => isNumber(v) || isNull(v);
|
|
29
35
|
|
|
30
|
-
const formatAmountIfSet = (
|
|
36
|
+
const formatAmountIfSet = (
|
|
37
|
+
amount: number | null | undefined,
|
|
38
|
+
currency: string,
|
|
39
|
+
locale: string,
|
|
40
|
+
maxLengthOverride: number | undefined,
|
|
41
|
+
) => {
|
|
31
42
|
if (maxLengthOverride) {
|
|
32
|
-
return amount
|
|
43
|
+
return amount != null ? String(amount) : '';
|
|
33
44
|
} else {
|
|
34
45
|
return typeof amount === 'number' ? formatAmount(amount, currency, locale) : '';
|
|
35
46
|
}
|
|
36
47
|
};
|
|
37
48
|
|
|
38
|
-
const parseNumber = (
|
|
49
|
+
const parseNumber = (
|
|
50
|
+
amount: string,
|
|
51
|
+
currency: string,
|
|
52
|
+
locale: string,
|
|
53
|
+
maxLengthOverride: number | undefined,
|
|
54
|
+
) => {
|
|
39
55
|
if (!maxLengthOverride) {
|
|
40
56
|
return parseAmount(amount, currency, locale);
|
|
41
57
|
}
|
|
42
58
|
if (maxLengthOverride && amount.length > maxLengthOverride) {
|
|
43
59
|
return 0;
|
|
44
60
|
}
|
|
45
|
-
return
|
|
61
|
+
return Number(amount);
|
|
46
62
|
};
|
|
47
63
|
|
|
48
64
|
const inputKeyCodeAllowlist = new Set([
|
|
@@ -61,39 +77,76 @@ const inputKeyCodeAllowlist = new Set([
|
|
|
61
77
|
|
|
62
78
|
const inputKeyAllowlist = new Set([keyValues.PERIOD, keyValues.COMMA]);
|
|
63
79
|
|
|
64
|
-
|
|
65
|
-
|
|
80
|
+
export interface MoneyInputProps extends WrappedComponentProps {
|
|
81
|
+
id?: string;
|
|
82
|
+
currencies: readonly Currency[];
|
|
83
|
+
selectedCurrency: CurrencyOption;
|
|
84
|
+
onCurrencyChange?: (value: CurrencyOption) => void;
|
|
85
|
+
placeholder?: number;
|
|
86
|
+
amount?: number;
|
|
87
|
+
size?: SizeSmall | SizeMedium | SizeLarge;
|
|
88
|
+
onAmountChange?: (value: number | null) => void;
|
|
89
|
+
addon?: React.ReactNode;
|
|
90
|
+
searchPlaceholder?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Allows the consumer to react to searching, while the search itself is handled internally.
|
|
93
|
+
*/
|
|
94
|
+
onSearchChange?: (value: { searchQuery: string; filteredOptions: Currency[] }) => void; // TODO
|
|
95
|
+
customActionLabel?: React.ReactNode;
|
|
96
|
+
onCustomAction?: () => void;
|
|
97
|
+
classNames?: Record<string, string>;
|
|
98
|
+
selectProps?: Partial<SelectInputProps<CurrencyOption>>;
|
|
99
|
+
maxLengthOverride?: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface MoneyInputState {
|
|
103
|
+
searchQuery: string;
|
|
104
|
+
formattedAmount: string;
|
|
105
|
+
locale: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
class MoneyInput extends Component<MoneyInputProps, MoneyInputState> {
|
|
109
|
+
declare props: MoneyInputProps &
|
|
110
|
+
Required<Pick<MoneyInputProps, keyof typeof MoneyInput.defaultProps>>;
|
|
111
|
+
|
|
112
|
+
static defaultProps = {
|
|
113
|
+
size: Size.LARGE,
|
|
114
|
+
classNames: {},
|
|
115
|
+
selectProps: {},
|
|
116
|
+
} satisfies Partial<MoneyInputProps>;
|
|
117
|
+
|
|
118
|
+
amountFocused = false;
|
|
119
|
+
|
|
120
|
+
constructor(props: MoneyInputProps) {
|
|
66
121
|
super(props);
|
|
67
|
-
const { locale } = this.props.intl;
|
|
68
|
-
this.formatMessage = this.props.intl.formatMessage;
|
|
69
122
|
this.state = {
|
|
70
123
|
searchQuery: '',
|
|
71
124
|
formattedAmount: formatAmountIfSet(
|
|
72
125
|
props.amount,
|
|
73
126
|
props.selectedCurrency.currency,
|
|
74
|
-
locale,
|
|
127
|
+
props.intl.locale,
|
|
75
128
|
props.maxLengthOverride,
|
|
76
129
|
),
|
|
77
|
-
locale,
|
|
130
|
+
locale: props.intl.locale,
|
|
78
131
|
};
|
|
79
132
|
}
|
|
80
133
|
|
|
81
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
82
|
-
this.setState({ locale: nextProps
|
|
134
|
+
UNSAFE_componentWillReceiveProps(nextProps: MoneyInputProps) {
|
|
135
|
+
this.setState({ locale: nextProps.intl.locale });
|
|
83
136
|
|
|
84
137
|
if (!this.amountFocused) {
|
|
85
138
|
this.setState({
|
|
86
139
|
formattedAmount: formatAmountIfSet(
|
|
87
140
|
nextProps.amount,
|
|
88
141
|
nextProps.selectedCurrency.currency,
|
|
89
|
-
nextProps
|
|
142
|
+
nextProps.intl.locale,
|
|
90
143
|
nextProps.maxLengthOverride,
|
|
91
144
|
),
|
|
92
145
|
});
|
|
93
146
|
}
|
|
94
147
|
}
|
|
95
148
|
|
|
96
|
-
isInputAllowedForKeyEvent = (event) => {
|
|
149
|
+
isInputAllowedForKeyEvent = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
97
150
|
const { keyCode, metaKey, key, ctrlKey } = event;
|
|
98
151
|
const isNumberKey = isNumber(parseInt(key, 10));
|
|
99
152
|
|
|
@@ -106,14 +159,14 @@ class MoneyInput extends Component {
|
|
|
106
159
|
);
|
|
107
160
|
};
|
|
108
161
|
|
|
109
|
-
handleKeyDown = (event) => {
|
|
162
|
+
handleKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
|
|
110
163
|
if (!this.isInputAllowedForKeyEvent(event)) {
|
|
111
164
|
event.preventDefault();
|
|
112
165
|
}
|
|
113
166
|
};
|
|
114
167
|
|
|
115
|
-
handlePaste = (event) => {
|
|
116
|
-
const paste =
|
|
168
|
+
handlePaste: React.ClipboardEventHandler<HTMLInputElement> = (event) => {
|
|
169
|
+
const paste = event.clipboardData.getData('text');
|
|
117
170
|
const { locale } = this.state;
|
|
118
171
|
const parsed = isEmpty(paste)
|
|
119
172
|
? null
|
|
@@ -133,13 +186,13 @@ class MoneyInput extends Component {
|
|
|
133
186
|
this.props.maxLengthOverride,
|
|
134
187
|
),
|
|
135
188
|
});
|
|
136
|
-
this.props.onAmountChange(parsed);
|
|
189
|
+
this.props.onAmountChange?.(parsed);
|
|
137
190
|
}
|
|
138
191
|
|
|
139
192
|
event.preventDefault();
|
|
140
193
|
};
|
|
141
194
|
|
|
142
|
-
onAmountChange = (event) => {
|
|
195
|
+
onAmountChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
143
196
|
const { value } = event.target;
|
|
144
197
|
this.setState({
|
|
145
198
|
formattedAmount: value,
|
|
@@ -153,7 +206,7 @@ class MoneyInput extends Component {
|
|
|
153
206
|
this.props.maxLengthOverride,
|
|
154
207
|
);
|
|
155
208
|
if (isNumberOrNull(parsed)) {
|
|
156
|
-
this.props.onAmountChange(parsed);
|
|
209
|
+
this.props.onAmountChange?.(parsed);
|
|
157
210
|
}
|
|
158
211
|
};
|
|
159
212
|
|
|
@@ -166,34 +219,26 @@ class MoneyInput extends Component {
|
|
|
166
219
|
this.amountFocused = true;
|
|
167
220
|
};
|
|
168
221
|
|
|
169
|
-
mapOption = (item) => {
|
|
170
|
-
return {
|
|
171
|
-
type: 'option',
|
|
172
|
-
value: item,
|
|
173
|
-
filterMatchers: [item.value, item.label, item.note, item.searchable],
|
|
174
|
-
};
|
|
175
|
-
};
|
|
176
|
-
|
|
177
222
|
getSelectOptions() {
|
|
178
|
-
const selectOptions =
|
|
223
|
+
const selectOptions = filterCurrenciesForQuery(this.props.currencies, this.state.searchQuery);
|
|
179
224
|
|
|
180
|
-
|
|
181
|
-
let
|
|
225
|
+
const formattedOptions: SelectInputItem<CurrencyOption>[] = [];
|
|
226
|
+
let currentGroupOptions: SelectInputOptionItem<CurrencyOption>[] | undefined;
|
|
182
227
|
|
|
183
228
|
selectOptions.forEach((item) => {
|
|
184
|
-
if (item.header) {
|
|
229
|
+
if (item.header != null) {
|
|
230
|
+
currentGroupOptions = [];
|
|
185
231
|
formattedOptions.push({
|
|
186
232
|
type: 'group',
|
|
187
233
|
label: item.header,
|
|
188
|
-
options:
|
|
234
|
+
options: currentGroupOptions,
|
|
189
235
|
});
|
|
190
|
-
groupIndex = formattedOptions.length - 1;
|
|
191
236
|
} else {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
237
|
+
(currentGroupOptions ?? formattedOptions).push({
|
|
238
|
+
type: 'option',
|
|
239
|
+
value: item,
|
|
240
|
+
filterMatchers: [item.value, item.label, item.note ?? '', item.searchable ?? ''],
|
|
241
|
+
});
|
|
197
242
|
}
|
|
198
243
|
});
|
|
199
244
|
|
|
@@ -224,29 +269,25 @@ class MoneyInput extends Component {
|
|
|
224
269
|
});
|
|
225
270
|
}
|
|
226
271
|
|
|
227
|
-
handleSelectChange = (value) => {
|
|
272
|
+
handleSelectChange = (value: CurrencyOption) => {
|
|
228
273
|
this.handleSearchChange('');
|
|
229
|
-
this.props.onCurrencyChange(value);
|
|
274
|
+
this.props.onCurrencyChange?.(value);
|
|
230
275
|
};
|
|
231
276
|
|
|
232
277
|
handleCustomAction = () => {
|
|
233
278
|
this.handleSearchChange('');
|
|
234
|
-
|
|
235
|
-
this.props.onCustomAction();
|
|
236
|
-
}
|
|
279
|
+
this.props.onCustomAction?.();
|
|
237
280
|
};
|
|
238
281
|
|
|
239
|
-
handleSearchChange = (searchQuery) => {
|
|
282
|
+
handleSearchChange = (searchQuery: string) => {
|
|
240
283
|
this.setState({ searchQuery });
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
});
|
|
246
|
-
}
|
|
284
|
+
this.props.onSearchChange?.({
|
|
285
|
+
searchQuery,
|
|
286
|
+
filteredOptions: filterCurrenciesForQuery(this.props.currencies, searchQuery),
|
|
287
|
+
});
|
|
247
288
|
};
|
|
248
289
|
|
|
249
|
-
style = (className) => this.props.classNames[className] || className;
|
|
290
|
+
style = (className: string) => this.props.classNames[className] || className;
|
|
250
291
|
|
|
251
292
|
render() {
|
|
252
293
|
const { selectedCurrency, onCurrencyChange, size, addon, id, selectProps, maxLengthOverride } =
|
|
@@ -364,13 +405,13 @@ class MoneyInput extends Component {
|
|
|
364
405
|
this.props.onCustomAction
|
|
365
406
|
? () => (
|
|
366
407
|
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
|
|
367
|
-
<div role="button" tabIndex=
|
|
408
|
+
<div role="button" tabIndex={0} onClick={this.handleCustomAction}>
|
|
368
409
|
{this.props.customActionLabel}
|
|
369
410
|
</div>
|
|
370
411
|
)
|
|
371
|
-
:
|
|
412
|
+
: undefined
|
|
372
413
|
}
|
|
373
|
-
placeholder={this.formatMessage(messages.selectPlaceholder)}
|
|
414
|
+
placeholder={this.props.intl.formatMessage(messages.selectPlaceholder)}
|
|
374
415
|
filterable
|
|
375
416
|
filterPlaceholder={this.props.searchPlaceholder}
|
|
376
417
|
disabled={disabled}
|
|
@@ -388,33 +429,31 @@ class MoneyInput extends Component {
|
|
|
388
429
|
}
|
|
389
430
|
}
|
|
390
431
|
|
|
391
|
-
function
|
|
432
|
+
function filterCurrenciesForQuery(currencies: readonly Currency[], query: string): Currency[] {
|
|
392
433
|
if (!query) {
|
|
393
|
-
return
|
|
434
|
+
return [...currencies];
|
|
394
435
|
}
|
|
395
436
|
|
|
437
|
+
const options = currencies.filter((option): option is CurrencyOption => option.header == null);
|
|
396
438
|
const filteredOptions = removeDuplicateValueOptions(options).filter((option) =>
|
|
397
|
-
|
|
439
|
+
currencyOptionFitsQuery(option, query),
|
|
398
440
|
);
|
|
399
441
|
|
|
400
442
|
return sortOptionsLabelsToFirst(filteredOptions, query);
|
|
401
443
|
}
|
|
402
444
|
|
|
403
|
-
function removeDuplicateValueOptions(options) {
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
result.push(option);
|
|
410
|
-
resultValues.push(option.value);
|
|
445
|
+
function removeDuplicateValueOptions(options: CurrencyOption[]) {
|
|
446
|
+
const uniqueValues = new Set<string>();
|
|
447
|
+
return options.filter((option) => {
|
|
448
|
+
if (!uniqueValues.has(option.value)) {
|
|
449
|
+
uniqueValues.add(option.value);
|
|
450
|
+
return true;
|
|
411
451
|
}
|
|
452
|
+
return false;
|
|
412
453
|
});
|
|
413
|
-
|
|
414
|
-
return result;
|
|
415
454
|
}
|
|
416
455
|
|
|
417
|
-
function
|
|
456
|
+
function currencyOptionFitsQuery(option: CurrencyOption, query: string) {
|
|
418
457
|
if (!option.value) {
|
|
419
458
|
return false;
|
|
420
459
|
}
|
|
@@ -426,11 +465,11 @@ function isCurrencyOptionAndFitsQuery(option, query) {
|
|
|
426
465
|
);
|
|
427
466
|
}
|
|
428
467
|
|
|
429
|
-
function contains(property, query) {
|
|
468
|
+
function contains(property: string | undefined, query: string) {
|
|
430
469
|
return property && property.toLowerCase().includes(query.toLowerCase());
|
|
431
470
|
}
|
|
432
471
|
|
|
433
|
-
function sortOptionsLabelsToFirst(options, query) {
|
|
472
|
+
function sortOptionsLabelsToFirst(options: CurrencyOption[], query: string) {
|
|
434
473
|
return options.sort((first, second) => {
|
|
435
474
|
const firstContains = contains(first.label, query);
|
|
436
475
|
const secondContains = contains(second.label, query);
|
|
@@ -448,47 +487,4 @@ function sortOptionsLabelsToFirst(options, query) {
|
|
|
448
487
|
});
|
|
449
488
|
}
|
|
450
489
|
|
|
451
|
-
MoneyInput.propTypes = {
|
|
452
|
-
id: PropTypes.string,
|
|
453
|
-
currencies: PropTypes.arrayOf(Currency).isRequired,
|
|
454
|
-
selectedCurrency: Currency.isRequired,
|
|
455
|
-
onCurrencyChange: PropTypes.func,
|
|
456
|
-
placeholder: PropTypes.number,
|
|
457
|
-
amount: PropTypes.number,
|
|
458
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
459
|
-
onAmountChange: PropTypes.func,
|
|
460
|
-
addon: PropTypes.node,
|
|
461
|
-
searchPlaceholder: PropTypes.string,
|
|
462
|
-
/**
|
|
463
|
-
* Allows the consumer to react to searching, while the search itself is handled internally. Called with `{ searchQuery: string, filteredOptions: Currency[] }`
|
|
464
|
-
*/
|
|
465
|
-
onSearchChange: PropTypes.func,
|
|
466
|
-
customActionLabel: PropTypes.node,
|
|
467
|
-
onCustomAction: PropTypes.func,
|
|
468
|
-
classNames: PropTypes.objectOf(PropTypes.string),
|
|
469
|
-
selectProps: PropTypes.object,
|
|
470
|
-
maxLengthOverride: PropTypes.number,
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
MoneyInput.defaultProps = {
|
|
474
|
-
id: null,
|
|
475
|
-
size: Size.LARGE,
|
|
476
|
-
addon: null,
|
|
477
|
-
searchPlaceholder: '',
|
|
478
|
-
onSearchChange: undefined,
|
|
479
|
-
onCurrencyChange: null,
|
|
480
|
-
placeholder: null,
|
|
481
|
-
amount: null,
|
|
482
|
-
onAmountChange: null,
|
|
483
|
-
customActionLabel: '',
|
|
484
|
-
onCustomAction: null,
|
|
485
|
-
classNames: {},
|
|
486
|
-
selectProps: {},
|
|
487
|
-
maxLengthOverride: null,
|
|
488
|
-
};
|
|
489
|
-
|
|
490
|
-
// this export is necessary for react-to-typescript-definitions
|
|
491
|
-
// to be able to properly generate TS types, this is due to us wrapping this component in `injectIntl` before exporting
|
|
492
|
-
export { MoneyInput };
|
|
493
|
-
|
|
494
490
|
export default injectIntl(MoneyInput);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { formatAmount, parseAmount } from './currencyFormatting';
|
|
2
2
|
|
|
3
3
|
jest.mock('@transferwise/formatting', () => ({
|
|
4
|
-
formatAmount: (number) => `formatted ${number}`,
|
|
4
|
+
formatAmount: (number: number) => `formatted ${number}`,
|
|
5
5
|
}));
|
|
6
6
|
|
|
7
7
|
describe('Number formatting', () => {
|
|
8
8
|
it('uses @transferwise/formatting for formatting numbers', () => {
|
|
9
|
-
expect(formatAmount(100)).toBe('formatted 100');
|
|
9
|
+
expect(formatAmount(100, 'gbp')).toBe('formatted 100');
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
it('parses localized numbers', () => {
|
|
@@ -5,7 +5,7 @@ import { DEFAULT_LOCALE } from '../common/locale';
|
|
|
5
5
|
export { formatAmount };
|
|
6
6
|
|
|
7
7
|
// TODO: do not duplicate this between formatting and components
|
|
8
|
-
const currencyDecimals = {
|
|
8
|
+
const currencyDecimals: Record<string, number> = {
|
|
9
9
|
BIF: 0,
|
|
10
10
|
BYR: 0,
|
|
11
11
|
CLP: 0,
|
|
@@ -41,30 +41,27 @@ function isNumberLocaleSupported() {
|
|
|
41
41
|
return numberString === '1,234';
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function getValidLocale(locale) {
|
|
44
|
+
function getValidLocale(locale: string) {
|
|
45
45
|
try {
|
|
46
46
|
const noUnderscoreLocale = locale.replace(/_/, '-');
|
|
47
47
|
|
|
48
48
|
Intl.NumberFormat(noUnderscoreLocale);
|
|
49
49
|
return noUnderscoreLocale;
|
|
50
50
|
} catch {
|
|
51
|
-
return
|
|
51
|
+
return DEFAULT_LOCALE;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function getCurrencyDecimals(currency
|
|
55
|
+
function getCurrencyDecimals(currency: string) {
|
|
56
56
|
const upperCaseCurrency = currency.toUpperCase();
|
|
57
|
-
|
|
58
|
-
return currencyDecimals[upperCaseCurrency];
|
|
59
|
-
}
|
|
60
|
-
return DEFAULT_CURRENCY_DECIMALS;
|
|
57
|
+
return currencyDecimals[upperCaseCurrency] ?? DEFAULT_CURRENCY_DECIMALS;
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
function getDecimalSeparator(locale) {
|
|
60
|
+
function getDecimalSeparator(locale: string) {
|
|
64
61
|
return isNumberLocaleSupported() ? (1.1).toLocaleString(locale)[1] : '.';
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
export function parseAmount(number, currency, locale) {
|
|
64
|
+
export function parseAmount(number: string, currency: string, locale = DEFAULT_LOCALE) {
|
|
68
65
|
const validLocale = getValidLocale(locale);
|
|
69
66
|
|
|
70
67
|
const precision = getCurrencyDecimals(currency);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`Popover on desktop renders when is open 1`] = `
|
|
4
4
|
<div
|
|
5
|
-
class="np-panel np-panel--open np-
|
|
5
|
+
class="np-panel np-panel--open np-popover__container"
|
|
6
6
|
data-popper-escaped="true"
|
|
7
7
|
data-popper-placement="right"
|
|
8
8
|
data-popper-reference-hidden="true"
|
|
@@ -1,107 +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
|
-
align-items: center;
|
|
47
|
-
text-align: center;
|
|
48
|
-
vertical-align: middle;
|
|
49
|
-
min-height: 40px;
|
|
50
|
-
min-height: var(--size-40);
|
|
51
|
-
line-height: 40px;
|
|
52
|
-
line-height: var(--size-40);
|
|
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
|
-
white-space: nowrap;
|
|
97
|
-
color: var(--color-interactive-primary);
|
|
98
|
-
}
|
|
99
|
-
.segmented-control__segments--nav {
|
|
100
|
-
list-style: none;
|
|
101
|
-
}
|
|
102
|
-
.segmented-control__segments--nav li,
|
|
103
|
-
.segmented-control__segments--nav ui {
|
|
104
|
-
flex-grow: 1;
|
|
105
|
-
margin: 0;
|
|
106
|
-
padding: 0;
|
|
107
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
|
-
type SegmentBase = {
|
|
3
|
-
id: string;
|
|
4
|
-
label: string;
|
|
5
|
-
value: string;
|
|
6
|
-
};
|
|
7
|
-
type Segment = SegmentBase & {
|
|
8
|
-
controls?: never;
|
|
9
|
-
};
|
|
10
|
-
type SegmentWithControls = SegmentBase & {
|
|
11
|
-
controls: string;
|
|
12
|
-
};
|
|
13
|
-
export type Segments = Segment[] | SegmentWithControls[];
|
|
14
|
-
type SegmentedControlPropsBase = {
|
|
15
|
-
name: string;
|
|
16
|
-
defaultValue: string;
|
|
17
|
-
type: 'input' | 'navigation';
|
|
18
|
-
onChange: (value: string) => void;
|
|
19
|
-
};
|
|
20
|
-
type SegmentedControlNavigationProps = {
|
|
21
|
-
type: 'navigation';
|
|
22
|
-
segments: SegmentWithControls[];
|
|
23
|
-
};
|
|
24
|
-
type SegmentedControlInputProps = {
|
|
25
|
-
type: 'input';
|
|
26
|
-
segments: Segment[];
|
|
27
|
-
};
|
|
28
|
-
export type SegmentedControlProps = SegmentedControlPropsBase & (SegmentedControlNavigationProps | SegmentedControlInputProps);
|
|
29
|
-
export declare const SegmentedControl: ({ name, defaultValue, type, segments, onChange, }: SegmentedControlProps) => import("react").JSX.Element;
|
|
30
|
-
export {};
|
|
31
|
-
//# sourceMappingURL=SegmentedControl.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../src/segmentedControl/SegmentedControl.tsx"],"names":[],"mappings":";AAGA,KAAK,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,KAAK,OAAO,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAClD,KAAK,mBAAmB,GAAG,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG,OAAO,EAAE,GAAG,mBAAmB,EAAE,CAAC;AAEzD,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;IAC7B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,KAAK,+BAA+B,GAAG;IACrC,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,yBAAyB,GAC3D,CAAC,+BAA+B,GAAG,0BAA0B,CAAC,CAAC;AAEjE,eAAO,MAAM,gBAAgB,sDAM1B,qBAAqB,gCA6GvB,CAAC"}
|
package/src/moneyInput/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './MoneyInput';
|