@transferwise/components 0.0.0-experimental-5e97fd1 → 0.0.0-experimental-3d0a13d

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 (66) hide show
  1. package/build/index.esm.js +252 -184
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +253 -185
  4. package/build/index.js.map +1 -1
  5. package/build/types/common/textFormat/formatWithPattern/formatWithPattern.d.ts +1 -1
  6. package/build/types/common/textFormat/formatWithPattern/formatWithPattern.d.ts.map +1 -1
  7. package/build/types/common/textFormat/getCursorPositionAfterKeystroke/getCursorPositionAfterKeystroke.d.ts +1 -2
  8. package/build/types/common/textFormat/getCursorPositionAfterKeystroke/getCursorPositionAfterKeystroke.d.ts.map +1 -1
  9. package/build/types/common/textFormat/getSymbolsInPatternWithPosition/getSymbolsInPatternWithPosition.d.ts +1 -5
  10. package/build/types/common/textFormat/getSymbolsInPatternWithPosition/getSymbolsInPatternWithPosition.d.ts.map +1 -1
  11. package/build/types/common/textFormat/unformatWithPattern/unformatWithPattern.d.ts +1 -1
  12. package/build/types/common/textFormat/unformatWithPattern/unformatWithPattern.d.ts.map +1 -1
  13. package/build/types/index.d.ts +1 -0
  14. package/build/types/index.d.ts.map +1 -1
  15. package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +11 -5
  16. package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts.map +1 -1
  17. package/build/types/inputWithDisplayFormat/index.d.ts +1 -1
  18. package/build/types/inputWithDisplayFormat/index.d.ts.map +1 -1
  19. package/build/types/inputs/Input.d.ts +6 -8
  20. package/build/types/inputs/Input.d.ts.map +1 -1
  21. package/build/types/inputs/SelectInput.d.ts +2 -2
  22. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  23. package/build/types/moneyInput/MoneyInput.d.ts +45 -31
  24. package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
  25. package/build/types/moneyInput/MoneyInput.messages.d.ts +6 -6
  26. package/build/types/moneyInput/MoneyInput.messages.d.ts.map +1 -1
  27. package/build/types/moneyInput/currencyFormatting.d.ts +2 -2
  28. package/build/types/moneyInput/currencyFormatting.d.ts.map +1 -1
  29. package/build/types/moneyInput/index.d.ts +2 -1
  30. package/build/types/moneyInput/index.d.ts.map +1 -1
  31. package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts +11 -5
  32. package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts.map +1 -1
  33. package/build/types/textareaWithDisplayFormat/index.d.ts +1 -1
  34. package/build/types/textareaWithDisplayFormat/index.d.ts.map +1 -1
  35. package/build/types/withDisplayFormat/WithDisplayFormat.d.ts +82 -54
  36. package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
  37. package/build/types/withDisplayFormat/index.d.ts +1 -2
  38. package/build/types/withDisplayFormat/index.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/src/common/textFormat/formatWithPattern/{formatWithPattern.ts → formatWithPattern.js} +4 -8
  41. package/src/common/textFormat/getCursorPositionAfterKeystroke/{getCursorPositionAfterKeystroke.ts → getCursorPositionAfterKeystroke.js} +6 -7
  42. package/src/common/textFormat/getSymbolsInPatternWithPosition/{getSymbolsInPatternWithPosition.ts → getSymbolsInPatternWithPosition.js} +2 -7
  43. package/src/common/textFormat/unformatWithPattern/{unformatWithPattern.ts → unformatWithPattern.js} +2 -3
  44. package/src/index.ts +6 -0
  45. package/src/inputWithDisplayFormat/InputWithDisplayFormat.js +14 -0
  46. package/src/inputs/Input.tsx +11 -6
  47. package/src/inputs/SelectInput.tsx +2 -2
  48. package/src/moneyInput/{MoneyInput.rtl.spec.js → MoneyInput.rtl.spec.tsx} +4 -4
  49. package/src/moneyInput/MoneyInput.spec.js +109 -49
  50. package/src/moneyInput/MoneyInput.story.tsx +6 -14
  51. package/src/moneyInput/{MoneyInput.js → MoneyInput.tsx} +189 -173
  52. package/src/moneyInput/{currencyFormatting.spec.js → currencyFormatting.spec.ts} +2 -2
  53. package/src/moneyInput/{currencyFormatting.js → currencyFormatting.ts} +7 -10
  54. package/src/moneyInput/index.ts +7 -0
  55. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.js +14 -0
  56. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.spec.js +15 -0
  57. package/src/withDisplayFormat/{WithDisplayFormat.tsx → WithDisplayFormat.js} +135 -122
  58. package/src/withDisplayFormat/index.js +1 -0
  59. package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +0 -11
  60. package/src/moneyInput/index.js +0 -1
  61. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +0 -30
  62. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +0 -11
  63. package/src/withDisplayFormat/index.ts +0 -2
  64. /package/src/inputWithDisplayFormat/{index.ts → index.js} +0 -0
  65. /package/src/moneyInput/{MoneyInput.messages.js → MoneyInput.messages.ts} +0 -0
  66. /package/src/textareaWithDisplayFormat/{index.ts → index.js} +0 -0
@@ -1,4 +1,5 @@
1
- import { Component, KeyboardEvent, ClipboardEvent, ChangeEvent, FocusEvent } from 'react';
1
+ import PropTypes from 'prop-types';
2
+ import { Component } from 'react';
2
3
 
3
4
  import { HistoryNavigator } from '../common';
4
5
  import {
@@ -9,61 +10,14 @@ import {
9
10
  getDistanceToPreviousSymbol,
10
11
  getDistanceToNextSymbol,
11
12
  } from '../common/textFormat';
12
- import { InputProps } from '../inputs/Input';
13
- import { TextAreaProps } from '../inputs/TextArea';
14
13
 
15
- export type TextInputProps = {
16
- value?: string;
17
- displayPattern: string;
18
- /**
19
- * autocomplete hides our form help so we need to disable it when help text
20
- * is present. Chrome ignores autocomplete=off, the only way to disable it is
21
- * to provide an 'invalid' value, for which 'disabled' serves.
22
- */
23
- autoComplete?: InputProps['autoComplete'] | 'disabled';
24
- onChange?: (value: string | null) => void;
25
- onBlur?: (value: string | null) => void;
26
- onFocus?: (value: string | null) => void;
27
- } & InputProps;
28
-
29
- export type Props = typeof WithDisplayFormat.defaultProps & {
30
- render: (renderProps: InputProps | TextAreaProps) => JSX.Element;
31
- } & TextInputProps;
32
-
33
- export type EventType =
34
- | 'KeyDown'
35
- | 'Paste'
36
- | 'Cut'
37
- | 'Undo'
38
- | 'Redo'
39
- | 'Backspace'
40
- | 'Delete'
41
- | null;
42
-
43
- type State = {
44
- value: string;
45
- historyNavigator: HistoryNavigator;
46
- prevDisplayPattern?: string;
47
- triggerType?: EventType;
48
- triggerEvent?: KeyboardEvent<HTMLInputElement> | null;
49
- pastedLength?: number;
50
- selectionStart?: HTMLInputElement['selectionStart'];
51
- selectionEnd?: HTMLInputElement['selectionEnd'];
52
- };
53
-
54
- class WithDisplayFormat extends Component<Props, State> {
55
- static defaultProps = {
56
- autoComplete: 'off',
57
- displayPattern: '',
58
- type: 'text',
59
- value: '',
60
- };
61
-
62
- constructor(props: Props) {
14
+ class WithDisplayFormat extends Component {
15
+ constructor(props) {
63
16
  super(props);
64
- const unformattedValue = unformatWithPattern(props.value, props.displayPattern);
17
+ const { value, displayPattern } = props;
18
+ const unformattedValue = unformatWithPattern(value, displayPattern);
65
19
  this.state = {
66
- value: formatWithPattern(unformattedValue, props.displayPattern),
20
+ value: formatWithPattern(unformattedValue, displayPattern),
67
21
  historyNavigator: new HistoryNavigator(),
68
22
  prevDisplayPattern: props.displayPattern,
69
23
  triggerType: null,
@@ -71,11 +25,12 @@ class WithDisplayFormat extends Component<Props, State> {
71
25
  };
72
26
  }
73
27
 
74
- static getDerivedStateFromProps(
75
- { displayPattern }: Props,
76
- { prevDisplayPattern = displayPattern, value, historyNavigator }: State,
77
- ) {
78
- if (prevDisplayPattern !== displayPattern) {
28
+ static getDerivedStateFromProps(nextProps, previousState) {
29
+ const { displayPattern } = nextProps;
30
+ const { prevDisplayPattern } = previousState;
31
+ if (previousState.prevDisplayPattern !== displayPattern) {
32
+ const { value, historyNavigator } = previousState;
33
+
79
34
  const unFormattedValue = unformatWithPattern(value, prevDisplayPattern);
80
35
  historyNavigator.reset();
81
36
 
@@ -90,37 +45,32 @@ class WithDisplayFormat extends Component<Props, State> {
90
45
  return null;
91
46
  }
92
47
 
93
- getUserAction = (unformattedValue: string): EventType => {
48
+ getUserAction = (unformattedValue) => {
94
49
  const { triggerEvent, triggerType, value } = this.state;
95
50
  const { displayPattern } = this.props;
96
51
 
97
- if (triggerEvent) {
98
- const charCode = String.fromCharCode(triggerEvent.which).toLowerCase();
52
+ const charCode = String.fromCharCode(triggerEvent.which).toLowerCase();
99
53
 
100
- if (triggerType === 'Paste' || triggerType === 'Cut') {
101
- return triggerType;
102
- }
54
+ if (triggerType === 'Paste' || triggerType === 'Cut') {
55
+ return triggerType;
56
+ }
103
57
 
104
- if ((triggerEvent.ctrlKey || triggerEvent.metaKey) && charCode === 'z') {
105
- return triggerEvent.shiftKey ? 'Redo' : 'Undo';
106
- }
107
- // Detect mouse event redo
108
- if (triggerEvent.ctrlKey && charCode === 'd') {
109
- return 'Delete';
110
- }
58
+ if ((triggerEvent.ctrlKey || triggerEvent.metaKey) && charCode === 'z') {
59
+ return triggerEvent.shiftKey ? 'Redo' : 'Undo';
60
+ }
61
+ // Detect mouse event redo
62
+ if (triggerEvent.ctrlKey && charCode === 'd') {
63
+ return 'Delete';
64
+ }
111
65
 
112
- // Android Fix.
113
- if (
114
- typeof triggerEvent.key === 'undefined' &&
115
- unformattedValue.length <= unformatWithPattern(value, displayPattern).length
116
- ) {
66
+ // Android Fix.
67
+ if (typeof triggerEvent.key === 'undefined') {
68
+ if (unformattedValue.length <= unformatWithPattern(value, displayPattern).length) {
117
69
  return 'Backspace';
118
70
  }
119
- return triggerEvent.key as EventType;
120
- } else {
121
- // triggerEvent can be null only in case of "autofilling" (via password manager extension or browser build-in one) events
122
- return 'Paste';
123
71
  }
72
+
73
+ return triggerEvent.key;
124
74
  };
125
75
 
126
76
  resetEvent = () => {
@@ -131,7 +81,7 @@ class WithDisplayFormat extends Component<Props, State> {
131
81
  });
132
82
  };
133
83
 
134
- detectUndoRedo = (event: KeyboardEvent<HTMLInputElement>) => {
84
+ detectUndoRedo = (event) => {
135
85
  const charCode = String.fromCharCode(event.which).toLowerCase();
136
86
  if ((event.ctrlKey || event.metaKey) && charCode === 'z') {
137
87
  return event.shiftKey ? 'Redo' : 'Undo';
@@ -139,9 +89,9 @@ class WithDisplayFormat extends Component<Props, State> {
139
89
  return null;
140
90
  };
141
91
 
142
- handleOnKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
92
+ handleOnKeyDown = (event) => {
143
93
  event.persist();
144
- const { selectionStart, selectionEnd } = event.target as HTMLInputElement;
94
+ const { selectionStart, selectionEnd } = event.target;
145
95
  const { historyNavigator } = this.state;
146
96
  const { displayPattern } = this.props;
147
97
 
@@ -164,7 +114,7 @@ class WithDisplayFormat extends Component<Props, State> {
164
114
  }
165
115
  };
166
116
 
167
- handleOnPaste = (event: ClipboardEvent<HTMLInputElement>) => {
117
+ handleOnPaste = (event) => {
168
118
  const { displayPattern } = this.props;
169
119
  const pastedLength = unformatWithPattern(
170
120
  event.clipboardData.getData('Text'),
@@ -178,19 +128,23 @@ class WithDisplayFormat extends Component<Props, State> {
178
128
  this.setState({ triggerType: 'Cut' });
179
129
  };
180
130
 
181
- isKeyAllowed = (action: EventType) => {
131
+ isKeyAllowed = (action) => {
182
132
  const { displayPattern } = this.props;
183
133
  const symbolsInPattern = displayPattern.split('').filter((character) => character !== '*');
184
134
 
185
- return !symbolsInPattern.includes(action as string);
135
+ return !symbolsInPattern.includes(action);
186
136
  };
187
137
 
188
- handleOnChange = (event: ChangeEvent<HTMLInputElement>) => {
189
- const { historyNavigator, triggerType } = this.state;
138
+ handleOnChange = (event) => {
139
+ const { historyNavigator, triggerEvent, triggerType } = this.state;
190
140
  const { displayPattern, onChange } = this.props;
191
141
  const { value } = event.target;
192
142
  let unformattedValue = unformatWithPattern(value, displayPattern);
193
- const action = this.getUserAction(unformattedValue);
143
+ const action =
144
+ triggerEvent === null
145
+ ? // triggerEvent can be null only in case of "autofilling" (via password manager extension or browser build-in one) events
146
+ 'Paste'
147
+ : this.getUserAction(unformattedValue);
194
148
  if (!this.isKeyAllowed(action) || triggerType === 'Undo' || triggerType === 'Redo') {
195
149
  return;
196
150
  }
@@ -204,20 +158,19 @@ class WithDisplayFormat extends Component<Props, State> {
204
158
 
205
159
  this.handleCursorPositioning(action);
206
160
 
207
- this.setState({ value: newFormattedValue }, () => {
208
- this.resetEvent();
209
- if (onChange) {
210
- const broadcastValue = unformatWithPattern(newFormattedValue, displayPattern);
211
- onChange(broadcastValue);
212
- }
213
- });
161
+ const broadcastValue = unformatWithPattern(newFormattedValue, displayPattern);
162
+
163
+ this.setState({ value: newFormattedValue }, this.resetEvent(), onChange(broadcastValue));
214
164
  };
215
165
 
216
- handleOnBlur = (event: FocusEvent<HTMLInputElement>) => {
217
- this.props.onBlur?.(unformatWithPattern(event.target.value, this.props.displayPattern));
166
+ handleOnBlur = (event) => {
167
+ const { displayPattern, onBlur } = this.props;
168
+ if (onBlur) {
169
+ onBlur(unformatWithPattern(event.target.value, displayPattern));
170
+ }
218
171
  };
219
172
 
220
- handleOnFocus = (event: FocusEvent<HTMLInputElement>) => {
173
+ handleOnFocus = (event) => {
221
174
  const { displayPattern, onFocus } = this.props;
222
175
  if (onFocus) {
223
176
  this.handleOnChange(event);
@@ -225,14 +178,13 @@ class WithDisplayFormat extends Component<Props, State> {
225
178
  }
226
179
  };
227
180
 
228
- handleDelete = (unformattedValue: string, action: EventType) => {
181
+ handleDelete = (unformattedValue, action) => {
229
182
  const { displayPattern } = this.props;
230
183
  const { selectionStart, selectionEnd } = this.state;
231
184
  const newStack = [...unformattedValue];
232
185
  if (selectionStart === selectionEnd) {
233
186
  let startPosition =
234
- (selectionStart as number) -
235
- getCountOfSymbolsInSelection(0, selectionStart, displayPattern);
187
+ selectionStart - getCountOfSymbolsInSelection(0, selectionStart, displayPattern);
236
188
  let toDelete = 0;
237
189
 
238
190
  let count = getDistanceToNextSymbol(selectionStart, displayPattern);
@@ -251,49 +203,110 @@ class WithDisplayFormat extends Component<Props, State> {
251
203
  return newStack.join('');
252
204
  };
253
205
 
254
- handleCursorPositioning = (action: EventType) => {
206
+ handleCursorPositioning = (action) => {
255
207
  const { displayPattern } = this.props;
256
208
  const { triggerEvent, selectionStart, selectionEnd, pastedLength } = this.state;
257
209
 
258
210
  const cursorPosition = getCursorPositionAfterKeystroke(
259
211
  action,
260
- selectionStart as number,
261
- selectionEnd as number,
212
+ selectionStart,
213
+ selectionEnd,
262
214
  displayPattern,
263
- pastedLength as number,
215
+ pastedLength,
264
216
  );
265
217
 
266
218
  setTimeout(() => {
267
219
  if (triggerEvent) {
268
- (triggerEvent.target as HTMLInputElement).setSelectionRange(cursorPosition, cursorPosition);
220
+ triggerEvent.target.setSelectionRange(cursorPosition, cursorPosition);
269
221
  }
270
222
  this.setState({ selectionStart: cursorPosition, selectionEnd: cursorPosition });
271
223
  }, 0);
272
224
  };
273
225
 
274
226
  render() {
275
- return this.props.render({
276
- value: this.state.value,
277
- type: this.props.type,
278
- inputMode: this.props.inputMode,
279
- className: this.props.className,
280
- id: this.props.id,
281
- name: this.props.name,
282
- placeholder: this.props.placeholder,
283
- readOnly: this.props.readOnly,
284
- required: this.props.required,
285
- minLength: this.props.minLength,
286
- maxLength: this.props.maxLength,
287
- disabled: this.props.disabled,
288
- autoComplete: this.props.autoComplete,
227
+ const {
228
+ type,
229
+ inputMode,
230
+ className,
231
+ id,
232
+ name,
233
+ placeholder,
234
+ readOnly,
235
+ required,
236
+ minLength,
237
+ maxLength,
238
+ disabled,
239
+ autoComplete,
240
+ } = this.props;
241
+ const { value } = this.state;
242
+ const renderProps = {
243
+ type,
244
+ inputMode,
245
+ className,
246
+ id,
247
+ name,
248
+ placeholder,
249
+ readOnly,
250
+ required,
251
+ minLength,
252
+ maxLength,
253
+ disabled,
254
+ autoComplete,
255
+ value,
289
256
  onFocus: this.handleOnFocus,
290
257
  onBlur: this.handleOnBlur,
291
258
  onPaste: this.handleOnPaste,
292
259
  onKeyDown: this.handleOnKeyDown,
293
260
  onChange: this.handleOnChange,
294
261
  onCut: this.handleOnCut,
295
- });
262
+ };
263
+ return this.props.render(renderProps);
296
264
  }
297
265
  }
298
266
 
267
+ WithDisplayFormat.propTypes = {
268
+ /**
269
+ * autocomplete hides our form help so we need to disable it when help text
270
+ * is present. Chrome ignores autocomplete=off, the only way to disable it is
271
+ * to provide an 'invalid' value, for which 'disabled' serves.
272
+ */
273
+ autoComplete: PropTypes.oneOf(['on', 'off', 'disabled']),
274
+ className: PropTypes.string,
275
+ disabled: PropTypes.bool,
276
+ id: PropTypes.string,
277
+ maxLength: PropTypes.number,
278
+ minLength: PropTypes.number,
279
+ name: PropTypes.string,
280
+ onFocus: PropTypes.func,
281
+ onBlur: PropTypes.func,
282
+ onChange: PropTypes.func.isRequired,
283
+ placeholder: PropTypes.string,
284
+ readOnly: PropTypes.bool,
285
+ render: PropTypes.func.isRequired,
286
+ required: PropTypes.bool,
287
+ displayPattern: PropTypes.string,
288
+ type: PropTypes.string,
289
+ inputMode: PropTypes.string,
290
+ value: PropTypes.string,
291
+ };
292
+
293
+ WithDisplayFormat.defaultProps = {
294
+ autoComplete: 'off',
295
+ className: null,
296
+ disabled: false,
297
+ id: null,
298
+ maxLength: null,
299
+ minLength: null,
300
+ name: null,
301
+ placeholder: null,
302
+ readOnly: false,
303
+ required: false,
304
+ displayPattern: '',
305
+ type: 'text',
306
+ inputMode: null,
307
+ value: '',
308
+ onFocus: null,
309
+ onBlur: null,
310
+ };
311
+
299
312
  export default WithDisplayFormat;
@@ -0,0 +1 @@
1
+ export { default } from './WithDisplayFormat';
@@ -1,11 +0,0 @@
1
- import { Input, type InputProps } from '../inputs/Input';
2
- import WithDisplayFormat, { type TextInputProps } from '../withDisplayFormat';
3
-
4
- const InputWithDisplayFormat = (props: TextInputProps) => (
5
- <WithDisplayFormat
6
- {...props}
7
- render={(renderProps) => <Input {...(renderProps as InputProps)} />}
8
- />
9
- );
10
-
11
- export default InputWithDisplayFormat;
@@ -1 +0,0 @@
1
- export { default } from './MoneyInput';
@@ -1,30 +0,0 @@
1
- import { StoryObj } from '@storybook/react';
2
-
3
- import { userEvent, within } from '../test-utils';
4
-
5
- import TextareaWithDisplayFormat from './TextareaWithDisplayFormat';
6
-
7
- export default {
8
- component: TextareaWithDisplayFormat,
9
- title: 'Forms/TextareaWithDisplayFormat',
10
- };
11
-
12
- type Story = StoryObj<typeof TextareaWithDisplayFormat>;
13
-
14
- export const Basic: Story = {
15
- render: (args) => {
16
- return (
17
- <>
18
- <TextareaWithDisplayFormat
19
- value="0000"
20
- displayPattern="**** - **** - ****"
21
- onChange={console.log}
22
- />
23
- </>
24
- );
25
- },
26
- play: ({ canvasElement }) => {
27
- const canvas = within(canvasElement);
28
- userEvent.type(canvas.getByRole('textbox'), '111122223333');
29
- },
30
- };
@@ -1,11 +0,0 @@
1
- import { TextArea, type TextAreaProps } from '../inputs/TextArea';
2
- import WithDisplayFormat, { type TextInputProps } from '../withDisplayFormat';
3
-
4
- const TextareaWithDisplayFormat = (props: TextInputProps) => (
5
- <WithDisplayFormat
6
- {...props}
7
- render={(renderProps) => <TextArea {...(renderProps as TextAreaProps)} />}
8
- />
9
- );
10
-
11
- export default TextareaWithDisplayFormat;
@@ -1,2 +0,0 @@
1
- export { default } from './WithDisplayFormat';
2
- export type { Props as WithDisplayFormatProps, TextInputProps } from './WithDisplayFormat';