@transferwise/components 45.27.0 → 45.28.1

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.
Files changed (54) hide show
  1. package/build/i18n/cs.json +2 -0
  2. package/build/i18n/de.json +2 -0
  3. package/build/i18n/es.json +2 -0
  4. package/build/i18n/fr.json +2 -0
  5. package/build/i18n/hu.json +2 -0
  6. package/build/i18n/id.json +2 -0
  7. package/build/i18n/it.json +2 -0
  8. package/build/i18n/ja.json +2 -0
  9. package/build/i18n/pl.json +2 -0
  10. package/build/i18n/pt.json +5 -3
  11. package/build/i18n/ro.json +5 -3
  12. package/build/i18n/ru.json +3 -1
  13. package/build/i18n/th.json +2 -0
  14. package/build/i18n/tr.json +2 -0
  15. package/build/i18n/uk.json +2 -0
  16. package/build/i18n/zh-CN.json +8 -6
  17. package/build/i18n/zh-HK.json +2 -0
  18. package/build/index.esm.js +1344 -1253
  19. package/build/index.esm.js.map +1 -1
  20. package/build/index.js +1343 -1252
  21. package/build/index.js.map +1 -1
  22. package/build/main.css +1 -1
  23. package/build/styles/main.css +1 -1
  24. package/build/styles/moneyInput/MoneyInput.css +1 -1
  25. package/build/types/inputs/SelectInput.d.ts +2 -1
  26. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  27. package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/i18n/cs.json +2 -0
  30. package/src/i18n/de.json +2 -0
  31. package/src/i18n/es.json +2 -0
  32. package/src/i18n/fr.json +2 -0
  33. package/src/i18n/hu.json +2 -0
  34. package/src/i18n/id.json +2 -0
  35. package/src/i18n/it.json +2 -0
  36. package/src/i18n/ja.json +2 -0
  37. package/src/i18n/pl.json +2 -0
  38. package/src/i18n/pt.json +5 -3
  39. package/src/i18n/ro.json +5 -3
  40. package/src/i18n/ru.json +3 -1
  41. package/src/i18n/th.json +2 -0
  42. package/src/i18n/tr.json +2 -0
  43. package/src/i18n/uk.json +2 -0
  44. package/src/i18n/zh-CN.json +8 -6
  45. package/src/i18n/zh-HK.json +2 -0
  46. package/src/inputs/SelectInput.spec.tsx +5 -5
  47. package/src/inputs/SelectInput.tsx +24 -3
  48. package/src/main.css +1 -1
  49. package/src/moneyInput/MoneyInput.css +1 -1
  50. package/src/moneyInput/MoneyInput.js +85 -42
  51. package/src/moneyInput/MoneyInput.less +40 -159
  52. package/src/moneyInput/MoneyInput.rtl.spec.js +71 -0
  53. package/src/moneyInput/MoneyInput.spec.js +247 -116
  54. package/src/moneyInput/MoneyInput.story.tsx +54 -1
@@ -1 +1 @@
1
- .tw-money-input .tw-money-input__fixed-currency{padding-left:0!important}[dir=rtl] .tw-money-input .tw-money-input__fixed-currency{padding-left:0!important;padding-right:0!important}.tw-money-input .tw-money-input__fixed-currency.input-md .currency-flag{margin-bottom:2px}.np-theme-personal .tw-money-input .tw-money-input__fixed-currency.input-md .currency-flag{margin-bottom:1px}.tw-money-input .tw-money-input__fixed-currency.input-lg .currency-flag{margin-bottom:4px}.np-theme-personal .tw-money-input .tw-money-input__fixed-currency.input-lg .currency-flag{margin-bottom:3px}.tw-money-input .tw-money-input__fixed-currency .currency-flag{vertical-align:middle}.tw-money-input .tw-money-input__fixed-currency .np-text-title-subsection{font-weight:var(--font-weight-normal)}.tw-money-input__keyline{background-color:#86a7bd1a;background-color:var(--color-background-neutral);border:1px solid #0000001a;border:1px solid var(--color-border-neutral);height:24px;height:var(--size-24);margin-right:24px;margin-right:var(--size-24);transform:translateY(4px);width:2px}[dir=rtl] .tw-money-input__keyline{margin-left:24px;margin-left:var(--size-24);margin-right:0}.tw-money-input__fixed-currency{position:relative}.tw-money-input__fixed-currency span{color:#5d7079;color:var(--color-content-secondary)}__text{font-size:1rem;font-size:var(--font-size-16)}.input-group-sm .np-text-title-subsection,.input-group-sm .tw-money-input__text{font-size:.875rem;font-size:var(--font-size-14)}.input-group-md .np-text-title-subsection{font-size:1rem;font-size:var(--font-size-16)}.input-group-lg .tw-money-input__text{font-size:1.375rem;font-size:var(--font-size-22)}.tw-money-input .amount-currency-select-btn>.np-select>.btn{align-items:center;display:flex;max-height:50px}.tw-money-input .amount-currency-select-btn>.np-select>.btn span:not(.tw-icon){font-weight:400;font-weight:var(--font-weight-regular);min-width:2.6em}.tw-money-input .amount-currency-select-btn>.np-select>.btn .currency-flag{display:inline-block!important;margin-left:2px;margin-right:2px}.tw-money-input .amount-currency-select-btn>.np-select>.np-dropdown-toggle{padding:0 16px;padding:0 var(--padding-small);text-align:center}.np-theme-personal .tw-money-input .amount-currency-select-btn>.np-select>.np-dropdown-toggle,.np-theme-personal .tw-money-input .amount-currency-select-btn>.np-select>.np-dropdown-toggle:active,.np-theme-personal .tw-money-input .amount-currency-select-btn>.np-select>.np-dropdown-toggle:hover:not(:focus){box-shadow:none}.np-theme-personal .tw-money-input .amount-currency-select-btn>.np-select>.np-dropdown-toggle .np-text-body-large{overflow:visible;text-overflow:clip}.np-theme-personal .tw-money-input .amount-currency-select-btn>.np-select>.np-dropdown-toggle .np-text-body-large .tw-money-input__text.np-text-title-subsection{font-weight:600;font-weight:var(--font-weight-semi-bold)}.tw-money-input .amount-currency-select-btn>.np-select>.np-btn>span.tw-icon{position:static}.np-theme-personal .tw-money-input__keyline{display:none}.np-theme-personal .tw-money-input.input-group-sm .form-control{min-height:34px}.np-theme-personal .tw-money-input.input-group-md .amount-currency-select-btn .btn-md,.np-theme-personal .tw-money-input.input-group-md .form-control{min-height:53px}.np-theme-personal .tw-money-input .tw-money-input__fixed-currency .np-text-title-subsection{color:#768e9c;color:var(--color-content-tertiary);font-weight:600;font-weight:var(--font-weight-semi-bold)}.np-theme-personal .tw-money-input button{border:none}.np-theme-personal .tw-money-input .np-text-title-subsection{color:var(--color-interactive-primary)}.np-theme-personal .tw-money-input .np-select-filter:focus{outline:none}.np-theme-personal .tw-money-input .amount-currency-select-btn .currency-flag{margin-right:0;margin-top:0}.np-theme-personal .tw-money-input .amount-currency-select-btn .np-btn-md .np-text-body-large{margin-left:4px;margin-right:4px}.np-theme-personal .tw-money-input .amount-currency-select-btn .np-btn-lg .np-text-body-large{margin-left:9px;margin-right:9px}
1
+ .tw-money-input{display:flex}.tw-money-input .tw-money-input__fixed-currency{padding-left:0!important}[dir=rtl] .tw-money-input .tw-money-input__fixed-currency{padding-left:0!important;padding-right:0!important}.tw-money-input .tw-money-input__fixed-currency .np-text-title-subsection{color:#768e9c;color:var(--color-content-tertiary);font-weight:600;font-weight:var(--font-weight-semi-bold)}.tw-money-input.input-group-sm .form-control{min-height:34px}.tw-money-input.input-group-sm .np-text-title-subsection{font-size:.875rem;font-size:var(--font-size-14)}.tw-money-input.input-group-md .form-control{min-height:53px}.tw-money-input.input-group-md .np-text-title-subsection{font-size:1rem;font-size:var(--font-size-16)}.tw-money-input .input-group-addon{flex-shrink:0}.tw-money-input .input-group-addon:not(.amount-currency-select-btn){align-items:center;display:flex;flex-shrink:0;width:auto}.tw-money-input .amount-currency-select-btn{flex-shrink:0;line-height:normal;width:auto}.tw-money-input .np-button-input{border-bottom-right-radius:10px!important;border-bottom-right-radius:var(--radius-small)!important;border-top-right-radius:10px!important;border-top-right-radius:var(--radius-small)!important}.tw-money-input .np-button-input:not(.disabled,:disabled):focus-visible{border-radius:10px!important;border-radius:var(--radius-small)!important}.has-error .tw-money-input .np-button-input,.has-error .tw-money-input .np-button-input:not(.disabled,:disabled):hover,.tw-money-input .np-button-input,.tw-money-input .np-button-input:active,.tw-money-input .np-button-input:not(.disabled,:disabled):hover{box-shadow:none}.tw-money-input:not(.disabled,:disabled):not(.np-button-input:active):has(.np-button-input:focus-visible){box-shadow:inset 0 0 0 1px #c9cbce!important;box-shadow:inset 0 0 0 1px var(--color-interactive-secondary)!important}
@@ -1,4 +1,5 @@
1
1
  import { isEmpty, isNumber, isNull } from '@transferwise/neptune-validation';
2
+ import { Flag } from '@wise/art';
2
3
  import classNames from 'classnames';
3
4
  import PropTypes from 'prop-types';
4
5
  import { Component } from 'react';
@@ -9,7 +10,7 @@ import { Key as keyValues } from '../common/key';
9
10
  import keyCodes from '../common/keyCodes';
10
11
  import { Size } from '../common/propsValues/size';
11
12
  import { Input } from '../inputs/Input';
12
- import Select from '../select';
13
+ import { SelectInput, SelectInputOptionContent } from '../inputs/SelectInput';
13
14
  import Title from '../title';
14
15
 
15
16
  import messages from './MoneyInput.messages';
@@ -23,7 +24,7 @@ const Currency = PropTypes.shape({
23
24
  note: PropTypes.string,
24
25
  searchable: PropTypes.string,
25
26
  });
26
- const CUSTOM_ACTION = 'CUSTOM_ACTION';
27
+
27
28
  const isNumberOrNull = (v) => isNumber(v) || isNull(v);
28
29
 
29
30
  const formatAmountIfSet = (amount, currency, locale, maxLengthOverride) => {
@@ -67,6 +68,7 @@ class MoneyInput extends Component {
67
68
  this.formatMessage = this.props.intl.formatMessage;
68
69
  this.state = {
69
70
  searchQuery: '',
71
+ selectedOption: this.props.selectedCurrency,
70
72
  formattedAmount: formatAmountIfSet(
71
73
  props.amount,
72
74
  props.selectedCurrency.currency,
@@ -165,14 +167,37 @@ class MoneyInput extends Component {
165
167
  this.amountFocused = true;
166
168
  };
167
169
 
170
+ mapOption = (item) => {
171
+ return {
172
+ type: 'option',
173
+ value: item,
174
+ filterMatchers: [item.value, item.label, item.note, item.searchable],
175
+ };
176
+ };
177
+
168
178
  getSelectOptions() {
169
179
  const selectOptions = [...filterOptionsForQuery(this.props.currencies, this.state.searchQuery)];
170
180
 
171
- if (this.props.onCustomAction) {
172
- selectOptions.push({ value: CUSTOM_ACTION, label: this.props.customActionLabel });
173
- }
174
-
175
- return selectOptions;
181
+ let formattedOptions = [];
182
+ let groupIndex = null;
183
+
184
+ selectOptions.forEach((item) => {
185
+ if (item.header) {
186
+ formattedOptions.push({
187
+ type: 'group',
188
+ label: item.header,
189
+ options: [],
190
+ });
191
+ groupIndex = formattedOptions.length - 1;
192
+ } else {
193
+ if (groupIndex === null) {
194
+ formattedOptions.push(this.mapOption(item));
195
+ } else {
196
+ formattedOptions[groupIndex]?.options.push(this.mapOption(item));
197
+ }
198
+ }
199
+ });
200
+ return formattedOptions;
176
201
  }
177
202
 
178
203
  setAmount() {
@@ -201,11 +226,14 @@ class MoneyInput extends Component {
201
226
 
202
227
  handleSelectChange = (value) => {
203
228
  this.handleSearchChange('');
229
+ this.setState({ selectedOption: value });
230
+ this.props.onCurrencyChange(value);
231
+ };
204
232
 
205
- if (this.props.onCustomAction && value.value === CUSTOM_ACTION) {
233
+ handleCustomAction = () => {
234
+ this.handleSearchChange('');
235
+ if (this.props.onCustomAction) {
206
236
  this.props.onCustomAction();
207
- } else {
208
- this.props.onCurrencyChange(value);
209
237
  }
210
238
  };
211
239
 
@@ -225,9 +253,25 @@ class MoneyInput extends Component {
225
253
  const { selectedCurrency, onCurrencyChange, size, addon, id, selectProps, maxLengthOverride } =
226
254
  this.props;
227
255
  const selectOptions = this.getSelectOptions();
256
+
257
+ const getFirstOption = () => {
258
+ if (selectOptions.length !== 0) {
259
+ const firstOption = selectOptions[0];
260
+ if (firstOption.type === 'option') {
261
+ return firstOption.value;
262
+ }
263
+ if (firstOption.type === 'group' && firstOption.options.length !== 0) {
264
+ return firstOption.options[0].value;
265
+ }
266
+ }
267
+ return null;
268
+ };
269
+
270
+ const firstOption = getFirstOption();
271
+
228
272
  const isFixedCurrency =
229
273
  !this.state.searchQuery &&
230
- ((selectOptions.length === 1 && selectOptions[0].currency === selectedCurrency.currency) ||
274
+ ((selectOptions.length === 1 && firstOption.currency === selectedCurrency.currency) ||
231
275
  !onCurrencyChange);
232
276
 
233
277
  const disabled = !this.props.onAmountChange;
@@ -279,16 +323,11 @@ class MoneyInput extends Component {
279
323
  )}
280
324
  >
281
325
  {(size === 'lg' || size === 'md') && (
282
- <>
283
- <i className={classNames(this.style('tw-money-input__keyline'))} />
284
- <i
285
- className={classNames(
286
- this.style('currency-flag'),
287
- this.style(`currency-flag-${selectedCurrency.currency.toLowerCase()}`),
288
- this.style('m-r-2'),
289
- )}
290
- />
291
- </>
326
+ <span
327
+ className={classNames(this.style('money-input-currency-flag'), this.style('m-r-2'))}
328
+ >
329
+ <Flag code={selectedCurrency.currency.toLowerCase()} intrinsicSize={24} />
330
+ </span>
292
331
  )}
293
332
  <Title
294
333
  as="span"
@@ -305,30 +344,34 @@ class MoneyInput extends Component {
305
344
  this.style('amount-currency-select-btn'),
306
345
  )}
307
346
  >
308
- <Select
309
- id={id ? `${id}-select` : undefined}
310
- classNames={this.props.classNames}
311
- options={selectOptions}
312
- selected={{
313
- ...selectedCurrency,
314
- label: (
315
- <Title
316
- as="span"
317
- type={Typography.TITLE_SUBSECTION}
318
- className="tw-money-input__text"
319
- >
320
- {selectedCurrency.label}
321
- </Title>
322
- ),
323
- note: null,
347
+ <SelectInput
348
+ items={selectOptions}
349
+ value={this.state.selectedOption}
350
+ compareValues="currency"
351
+ renderValue={(currency, withinTrigger) => {
352
+ return (
353
+ <SelectInputOptionContent
354
+ title={currency.label}
355
+ note={withinTrigger ? undefined : currency.note}
356
+ icon={<Flag code={currency.currency} intrinsicSize={24} />}
357
+ />
358
+ );
324
359
  }}
360
+ renderFooter={
361
+ this.props.onCustomAction
362
+ ? () => (
363
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events
364
+ <div role="button" tabIndex="0" onClick={this.handleCustomAction}>
365
+ {this.props.customActionLabel}
366
+ </div>
367
+ )
368
+ : null
369
+ }
325
370
  placeholder={this.formatMessage(messages.selectPlaceholder)}
326
- searchPlaceholder={this.props.searchPlaceholder}
327
- searchValue={this.state.searchQuery}
371
+ filterable
372
+ filterPlaceholder={this.props.searchPlaceholder}
373
+ disabled={disabled}
328
374
  size={size}
329
- required
330
- dropdownWidth={Size.LARGE}
331
- inverse
332
375
  onChange={this.handleSelectChange}
333
376
  onSearchChange={this.handleSearchChange}
334
377
  {...selectProps}
@@ -2,187 +2,68 @@
2
2
  @import (reference) "../../node_modules/@transferwise/neptune-css/src/less/mixins/_logical-properties.less";
3
3
 
4
4
  .tw-money-input {
5
+ display: flex;
5
6
  .tw-money-input__fixed-currency {
6
7
  .padding(left, 0) !important;
7
8
 
8
- // let's plan on getting rid of these custom margins
9
- // they center the flags for fixed money input currency flags
10
- // there's got to be a better way to do it
11
-
12
- &.input-md {
13
- .currency-flag {
14
- margin-bottom: 2px;
15
-
16
- .np-theme-personal & {
17
- margin-bottom: 1px;
18
- }
19
- }
20
- }
21
-
22
- &.input-lg {
23
- .currency-flag {
24
- margin-bottom: 4px;
25
-
26
- .np-theme-personal & {
27
- margin-bottom: 3px;
28
- }
29
- }
30
- }
31
-
32
- .currency-flag {
33
- vertical-align: middle;
34
- }
35
-
36
9
  .np-text-title-subsection {
37
- font-weight: var(--font-weight-normal);
38
- }
39
- }
40
-
41
- &__keyline {
42
- width: 2px;
43
- border: solid 1px var(--color-border-neutral);
44
- height: var(--size-24);
45
- background-color: var(--color-background-neutral);
46
- .margin(right, var(--size-24));
47
-
48
- transform: translateY(4px);
49
- }
50
-
51
- &__fixed-currency {
52
- span {
53
- color: var(--color-content-secondary);
10
+ color: var(--color-content-tertiary);
11
+ font-weight: var(--font-weight-semi-bold);
54
12
  }
55
-
56
- position: relative;
57
13
  }
58
- }
59
-
60
- &__text {
61
- font-size: var(--font-size-16);
62
- }
63
-
64
- &.input-group-sm {
65
- .tw-money-input__text {
66
- font-size: var(--font-size-14);
67
- }
68
-
69
- .np-text-title-subsection {
70
- font-size: var(--font-size-14);
71
- }
72
- }
73
-
74
- &.input-group-md {
75
- .np-text-title-subsection {
76
- font-size: var(--font-size-16);
77
- }
78
- }
79
-
80
- &.input-group-lg {
81
- .tw-money-input__text {
82
- font-size: var(--font-size-22);
83
- }
84
- }
85
-
86
- .tw-money-input .amount-currency-select-btn > .np-select > .btn {
87
- display: flex;
88
- align-items: center;
89
- max-height: 50px;
90
14
 
91
- span:not(.tw-icon) {
92
- min-width: 2.6em; // fits a 3 letter string, ZMW being the longest one
93
- font-weight: var(--font-weight-regular);
94
- }
95
-
96
- .currency-flag {
97
- margin-right: 2px;
98
- margin-left: 2px;
99
- display: inline-block !important; // override a hidden-xs in select option which has too much logic around it
100
- }
101
- }
102
-
103
- .tw-money-input .amount-currency-select-btn > .np-select > .np-dropdown-toggle {
104
- padding: 0 var(--padding-small);
105
- text-align: center;
106
-
107
- .np-theme-personal & {
108
- box-shadow: none;
109
-
110
- &:hover:not(:focus),
111
- &:active {
112
- box-shadow: none;
113
- }
114
-
115
- .np-text-body-large {
116
- .tw-money-input__text.np-text-title-subsection {
117
- font-weight: var(--font-weight-semi-bold);
118
- }
119
- overflow: visible;
120
- text-overflow: clip;
121
- }
122
- }
123
- }
124
-
125
- .tw-money-input .amount-currency-select-btn > .np-select > .np-btn > span.tw-icon {
126
- position: static;
127
- }
128
-
129
- .np-theme-personal {
130
- .tw-money-input__keyline {
131
- display: none;
132
- }
133
-
134
- .tw-money-input.input-group-sm {
15
+ &.input-group-sm {
135
16
  .form-control {
136
17
  min-height: 34px;
137
18
  }
19
+ .np-text-title-subsection {
20
+ font-size: var(--font-size-14);
21
+ }
138
22
  }
139
23
 
140
- .tw-money-input.input-group-md {
141
- .form-control,
142
- .amount-currency-select-btn .btn-md {
24
+ &.input-group-md {
25
+ .form-control {
143
26
  min-height: 53px;
144
27
  }
28
+ .np-text-title-subsection {
29
+ font-size: var(--font-size-16);
30
+ }
145
31
  }
146
32
 
147
- .tw-money-input {
148
- .tw-money-input__fixed-currency {
149
- .np-text-title-subsection {
150
- color: var(--color-content-tertiary);
151
- font-weight: var(--font-weight-semi-bold);
152
- }
33
+ .input-group-addon {
34
+ flex-shrink: 0;
35
+ &:not(.amount-currency-select-btn) {
36
+ display: flex;
37
+ align-items: center;
38
+ flex-shrink: 0;
39
+ width: auto;
153
40
  }
41
+ }
42
+
43
+ .amount-currency-select-btn {
44
+ flex-shrink: 0;
45
+ width: auto;
46
+ line-height: normal;
47
+ }
154
48
 
155
- button {
156
- border: none;
157
- }
49
+ .np-button-input {
50
+ border-top-right-radius: var(--radius-small) !important;
51
+ border-bottom-right-radius: var(--radius-small) !important;
158
52
 
159
- .np-text-title-subsection {
160
- color: var(--color-interactive-primary);
53
+ &:not(.disabled,:disabled):focus-visible {
54
+ border-radius: var(--radius-small) !important;
161
55
  }
162
56
 
163
- .np-select-filter:focus {
164
- outline: none;
57
+ &,
58
+ &:active,
59
+ &:not(.disabled,:disabled):hover,
60
+ .has-error &,
61
+ .has-error &:not(.disabled,:disabled):hover {
62
+ box-shadow: none;
165
63
  }
64
+ }
166
65
 
167
- .amount-currency-select-btn {
168
- .currency-flag {
169
- margin-right: 0;
170
- margin-top: 0;
171
- }
172
-
173
- .np-btn-md {
174
- .np-text-body-large {
175
- margin-left: 4px;
176
- margin-right: 4px;
177
- }
178
- }
179
-
180
- .np-btn-lg {
181
- .np-text-body-large {
182
- margin-left: 9px;
183
- margin-right: 9px;
184
- }
185
- }
186
- }
66
+ &:not(.disabled,:disabled):not(.np-button-input:active):has(.np-button-input:focus-visible) {
67
+ box-shadow: inset 0 0 0 1px var(--color-interactive-secondary) !important;
187
68
  }
188
69
  }
@@ -0,0 +1,71 @@
1
+ import { mockMatchMedia, mockResizeObserver, render, userEvent } from '../test-utils';
2
+
3
+ import MoneyInput from './MoneyInput';
4
+
5
+ mockMatchMedia();
6
+ mockResizeObserver();
7
+
8
+ describe('MoneyInput', () => {
9
+ const currencies = [
10
+ {
11
+ header: 'Popular currencies',
12
+ },
13
+ {
14
+ value: 'EUR',
15
+ label: 'EUR',
16
+ note: 'Euro',
17
+ currency: 'eur',
18
+ searchable: 'Spain, Germany, France, Austria, Estonia',
19
+ },
20
+ {
21
+ value: 'USD',
22
+ label: 'USD',
23
+ note: 'United States dollar',
24
+ currency: 'usd',
25
+ searchable: 'Hong Kong, Saudi Arabia',
26
+ },
27
+ {
28
+ value: 'GBP',
29
+ label: 'GBP',
30
+ note: 'British pound',
31
+ currency: 'gbp',
32
+ searchable: 'England, Scotland, Wales',
33
+ },
34
+ {
35
+ header: 'Some other currencies',
36
+ },
37
+ {
38
+ value: 'CAD',
39
+ label: 'CAD',
40
+ note: 'Canadian dollar',
41
+ currency: 'cad',
42
+ },
43
+ {
44
+ value: 'AUD',
45
+ label: 'AUD',
46
+ note: 'Australian dollar',
47
+ currency: 'aud',
48
+ },
49
+ ];
50
+ const props = {
51
+ currencies,
52
+ selectedCurrency: currencies[1],
53
+ amount: 1000,
54
+ onAmountChange: jest.fn(),
55
+ onCurrencyChange: jest.fn(),
56
+ };
57
+ it.each([
58
+ ['asd', ''],
59
+ ['1a2s3d', '123'],
60
+ ['±!@#$^*_+?><', ''],
61
+ ['1±!@#$^*,_+?><2', '1,2'],
62
+ ['12,3', '12,3'],
63
+ ['12.3', '12.3'],
64
+ ])("ignores the letters when typed '%s' and shows '%s'", (testValue, expectedValue) => {
65
+ const { container } = render(<MoneyInput {...props} amount={null} />);
66
+
67
+ const input = container.querySelector('input');
68
+ userEvent.type(input, testValue);
69
+ expect(input).toHaveValue(expectedValue);
70
+ });
71
+ });