@hero-design/rn-work-uikit 1.9.5 → 1.10.0

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 (45) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +219 -63
  3. package/assets/fonts/hero-icons-mobile.ttf +0 -0
  4. package/es/index.js +55337 -0
  5. package/lib/index.js +7173 -21259
  6. package/package.json +22 -22
  7. package/src/components/FormGroup/index.tsx +21 -10
  8. package/src/components/FormGroup/utils.ts +3 -3
  9. package/src/components/TextInput/InputRow.tsx +3 -6
  10. package/src/components/TextInput/index.tsx +4 -4
  11. package/src/components/TextInput/types.ts +4 -8
  12. package/types/index.d.ts +525 -0
  13. package/src/__tests__/index-export.spec.ts +0 -64
  14. package/src/__tests__/index.spec.tsx +0 -14
  15. package/src/components/DatePicker/__tests__/__snapshots__/index.spec.tsx.snap +0 -1649
  16. package/src/components/DatePicker/__tests__/index.spec.tsx +0 -56
  17. package/src/components/FormGroup/__tests__/__snapshots__/index.spec.tsx.snap +0 -908
  18. package/src/components/FormGroup/__tests__/index.spec.tsx +0 -319
  19. package/src/components/FormGroup/__tests__/utils.spec.ts +0 -73
  20. package/src/components/RichTextEditor/__tests__/EditorToolbar.spec.tsx +0 -154
  21. package/src/components/RichTextEditor/__tests__/MentionList.spec.tsx +0 -105
  22. package/src/components/RichTextEditor/__tests__/RichTextEditor.spec.tsx +0 -81
  23. package/src/components/RichTextEditor/__tests__/RichTextEditorInput.spec.tsx +0 -174
  24. package/src/components/RichTextEditor/__tests__/__snapshots__/EditorToolbar.spec.tsx.snap +0 -407
  25. package/src/components/RichTextEditor/__tests__/__snapshots__/MentionList.spec.tsx.snap +0 -13
  26. package/src/components/Select/__tests__/__snapshots__/index.spec.tsx.snap +0 -1324
  27. package/src/components/Select/__tests__/index.spec.tsx +0 -43
  28. package/src/components/TextInput/__tests__/ErrorOrHelpText.spec.tsx +0 -20
  29. package/src/components/TextInput/__tests__/FloatingLabel.spec.tsx +0 -190
  30. package/src/components/TextInput/__tests__/InputComponent.spec.tsx +0 -41
  31. package/src/components/TextInput/__tests__/InputRow.spec.tsx +0 -233
  32. package/src/components/TextInput/__tests__/MaxLengthMessage.spec.tsx +0 -17
  33. package/src/components/TextInput/__tests__/PrefixComponent.spec.tsx +0 -14
  34. package/src/components/TextInput/__tests__/StyledTextInput.spec.tsx +0 -114
  35. package/src/components/TextInput/__tests__/SuffixComponent.spec.tsx +0 -20
  36. package/src/components/TextInput/__tests__/__snapshots__/StyledTextInput.spec.tsx.snap +0 -583
  37. package/src/components/TextInput/__tests__/__snapshots__/index.spec.tsx.snap +0 -5835
  38. package/src/components/TextInput/__tests__/getState.spec.tsx +0 -89
  39. package/src/components/TextInput/__tests__/index.spec.tsx +0 -679
  40. package/src/components/TimePicker/__tests__/index.spec.tsx +0 -34
  41. package/src/theme/__tests__/ThemeProvider.spec.tsx +0 -32
  42. package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +0 -2042
  43. package/src/theme/__tests__/index.spec.ts +0 -7
  44. package/src/utils/__tests__/helpers.spec.ts +0 -92
  45. package/stats/1.3.0/rn-work-uikit-stats.html +0 -4842
@@ -1,679 +0,0 @@
1
- import React from 'react';
2
- import { act, fireEvent, waitFor, within } from '@testing-library/react-native';
3
- import type {
4
- TextInput as RNTextInput,
5
- ViewStyle,
6
- StyleProp,
7
- } from 'react-native';
8
- import { StyleSheet } from 'react-native';
9
- import { Icon } from '@hero-design/rn';
10
- import { theme } from '../../..';
11
- import renderWithTheme from '../../../../testUtils/renderWithTheme';
12
- import type { TextInputHandles } from '../index';
13
- import TextInput from '../index';
14
-
15
- describe('TextInput', () => {
16
- describe('when user sees an empty input field', () => {
17
- it('should display label and icons but hide input content until user interacts', () => {
18
- const { toJSON, getByTestId, queryAllByTestId } = renderWithTheme(
19
- <TextInput
20
- label="Amount (AUD)"
21
- prefix="dollar-sign"
22
- suffix="arrow-down"
23
- style={{ borderColor: theme.colors.error }}
24
- />
25
- );
26
-
27
- const textInput = getByTestId('text-input');
28
- expect(textInput).toBeTruthy();
29
-
30
- // Ensure all visual elements are present
31
- expect(queryAllByTestId('input-prefix')).toHaveLength(1);
32
- expect(queryAllByTestId('input-label')).toHaveLength(1);
33
- expect(queryAllByTestId('input-suffix')).toHaveLength(1);
34
-
35
- // User should see the label text
36
- const inputLabel = getByTestId('input-label');
37
- expect(inputLabel).toHaveTextContent('Amount (AUD) (Optional)');
38
-
39
- // User should be able to identify the prefix icon through accessibility
40
- const prefixIcon = getByTestId('input-prefix-icon');
41
- expect(prefixIcon).toBeTruthy();
42
- expect(prefixIcon).toHaveProp(
43
- 'accessibilityLabel',
44
- 'Prefix icon: dollar-sign'
45
- );
46
-
47
- expect(toJSON()).toMatchSnapshot();
48
- });
49
-
50
- it('should not show label when no label text is provided', () => {
51
- const { queryByTestId, queryAllByTestId } = renderWithTheme(
52
- <TextInput
53
- prefix="dollar-sign"
54
- suffix="arrow-down"
55
- testID="idle-text-input"
56
- />
57
- );
58
-
59
- // User should not see any label element when none is provided
60
- expect(queryAllByTestId('input-label')).toHaveLength(0);
61
-
62
- // Input elements should still be present but hidden until interaction
63
- expect(queryByTestId('text-input-idle-text-input')).toBeTruthy();
64
- expect(queryByTestId('input-prefix')).toBeTruthy();
65
- });
66
-
67
- it('should respond to user interactions with proper callbacks', () => {
68
- const onChangeText = jest.fn();
69
- const onBlur = jest.fn();
70
- const onFocus = jest.fn();
71
-
72
- const { getByTestId } = renderWithTheme(
73
- <TextInput
74
- label="Amount (AUD)"
75
- prefix="dollar-sign"
76
- suffix="arrow-down"
77
- onChangeText={onChangeText}
78
- onBlur={onBlur}
79
- onFocus={onFocus}
80
- />
81
- );
82
-
83
- const textInput = getByTestId('text-input');
84
-
85
- // User focuses the input
86
- fireEvent(textInput, 'focus');
87
- expect(onFocus).toHaveBeenCalledTimes(1);
88
-
89
- // User types text into the input
90
- fireEvent.changeText(textInput, 'Thong Quach');
91
- expect(onChangeText).toHaveBeenCalledWith('Thong Quach');
92
-
93
- // User moves focus away from the input
94
- fireEvent(textInput, 'blur');
95
- expect(onBlur).toHaveBeenCalledTimes(1);
96
- });
97
- });
98
-
99
- describe('when user sees input with custom prefix and suffix elements', () => {
100
- it('should display custom React components instead of icon names', () => {
101
- const { toJSON, getByTestId } = renderWithTheme(
102
- <TextInput
103
- label="Amount (AUD)"
104
- prefix={<Icon icon="eye-circle" testID="prefix-element" />}
105
- suffix={<Icon icon="eye-invisible" testID="suffix-element" />}
106
- required
107
- />
108
- );
109
-
110
- // User should see the label with an asterisk
111
- const textInput = getByTestId('text-input');
112
- expect(textInput).toBeTruthy();
113
-
114
- // User should see all visual elements including custom components
115
- expect(getByTestId('prefix-element')).toBeTruthy();
116
- expect(getByTestId('suffix-element')).toBeTruthy();
117
-
118
- expect(toJSON()).toMatchSnapshot();
119
- });
120
- });
121
-
122
- describe('when user sees a required field', () => {
123
- it('should indicate the field is required through styling', () => {
124
- const { toJSON, getByTestId } = renderWithTheme(
125
- <TextInput
126
- label="Amount (AUD)"
127
- prefix="dollar-sign"
128
- suffix="arrow-down"
129
- required
130
- />
131
- );
132
-
133
- // User should see the label text with an asterisk
134
- const textInput = getByTestId('text-input');
135
- expect(textInput).toBeTruthy();
136
-
137
- expect(toJSON()).toMatchSnapshot();
138
- });
139
- });
140
-
141
- describe('when user has entered text', () => {
142
- it('should show the input content and maintain all visual elements', () => {
143
- const { toJSON, getByDisplayValue } = renderWithTheme(
144
- <TextInput
145
- label="Amount (AUD)"
146
- prefix="dollar-sign"
147
- suffix="arrow-down"
148
- value="100"
149
- />
150
- );
151
-
152
- // User should see their entered value
153
- const textInput = getByDisplayValue('100');
154
- expect(textInput).toBeTruthy();
155
-
156
- expect(toJSON()).toMatchSnapshot();
157
- });
158
- });
159
-
160
- describe('when label is not provided', () => {
161
- it('should show the input content', () => {
162
- const { getByDisplayValue } = renderWithTheme(
163
- <TextInput value="This is the content" />
164
- );
165
- expect(getByDisplayValue('This is the content')).toBeTruthy();
166
- });
167
- });
168
-
169
- describe('when user encounters a read-only field', () => {
170
- it('should display content but prevent user from editing', () => {
171
- const onChangeText = jest.fn();
172
- const onFocus = jest.fn();
173
- const { toJSON, getByDisplayValue, getByTestId } = renderWithTheme(
174
- <TextInput
175
- label="Amount (AUD)"
176
- prefix="dollar-sign"
177
- suffix="arrow-down"
178
- editable={false}
179
- value="Read-only value"
180
- onChangeText={onChangeText}
181
- onFocus={onFocus}
182
- />
183
- );
184
-
185
- // User should see the content and label
186
- const textInput = getByTestId('text-input');
187
- expect(textInput).toBeTruthy();
188
- expect(getByDisplayValue('Read-only value')).toBeTruthy();
189
-
190
- // The input should have editable=false prop
191
- expect(textInput).toHaveProp('editable', false);
192
-
193
- // Simulate user trying to type
194
- fireEvent.changeText(textInput, 'New value');
195
- expect(onChangeText).not.toHaveBeenCalled();
196
-
197
- // It should still show the old value
198
- expect(textInput.props.value).toBe('Read-only value');
199
-
200
- // User should still see the original value displayed
201
- expect(getByDisplayValue('Read-only value')).toBeTruthy();
202
-
203
- expect(toJSON()).toMatchSnapshot();
204
- });
205
- });
206
-
207
- describe('when user sees a loading state', () => {
208
- it('should show loading indicator in place of suffix icon', () => {
209
- const { toJSON, getByTestId } = renderWithTheme(
210
- <TextInput
211
- label="Amount (AUD)"
212
- prefix="dollar-sign"
213
- suffix="arrow-down"
214
- loading
215
- value="100"
216
- />
217
- );
218
-
219
- const textInput = getByTestId('text-input');
220
- expect(textInput).toBeTruthy();
221
-
222
- // User should see loading indicator instead of regular suffix
223
- const suffixContainer = getByTestId('input-suffix');
224
- expect(suffixContainer).toBeTruthy();
225
-
226
- const loadingIcon = within(suffixContainer).getByLabelText(
227
- 'Suffix icon: loading'
228
- );
229
- expect(loadingIcon).toBeTruthy();
230
-
231
- expect(toJSON()).toMatchSnapshot();
232
- });
233
- });
234
-
235
- describe('when user sees a textarea with character count', () => {
236
- it('should display multiline input with character counter', () => {
237
- const { toJSON, getByTestId, getByText } = renderWithTheme(
238
- <TextInput
239
- label="Amount (AUD)"
240
- prefix="dollar-sign"
241
- suffix="arrow-down"
242
- value="100"
243
- maxLength={255}
244
- variant="textarea"
245
- />
246
- );
247
-
248
- // User should see the label and character count
249
- const textInput = getByTestId('text-input');
250
- expect(textInput).toBeTruthy();
251
- expect(getByText('3/255')).toBeTruthy();
252
-
253
- // User should be able to enter multiple lines
254
- expect(textInput).toHaveProp('multiline', true);
255
-
256
- expect(toJSON()).toMatchSnapshot();
257
- });
258
-
259
- it('should hide character count when user requests it', () => {
260
- const { toJSON, queryByText, getByTestId } = renderWithTheme(
261
- <TextInput
262
- label="Amount (AUD)"
263
- prefix="dollar-sign"
264
- suffix="arrow-down"
265
- value="100"
266
- maxLength={255}
267
- hideCharacterCount
268
- variant="textarea"
269
- />
270
- );
271
-
272
- // User should see the label but not the character count
273
- const textInput = getByTestId('text-input');
274
- expect(textInput).toBeTruthy();
275
- expect(queryByText('3/255')).toBeFalsy();
276
-
277
- // User should still be able to enter multiple lines
278
- expect(textInput).toHaveProp('multiline', true);
279
-
280
- expect(toJSON()).toMatchSnapshot();
281
- });
282
- });
283
-
284
- describe('when user encounters a disabled field', () => {
285
- it('should display content but prevent any user interaction', () => {
286
- const { toJSON, getByTestId } = renderWithTheme(
287
- <TextInput label="Amount (AUD)" disabled value="100" />
288
- );
289
-
290
- const textInput = getByTestId('text-input');
291
- expect(textInput).toBeDisabled();
292
-
293
- // Visual styling should indicate disabled state
294
- const borderElement = getByTestId('text-input-border');
295
- expect(borderElement).toBeTruthy();
296
-
297
- // Helper function to extract border color from complex React Native styles
298
- function getBorderColor(style: unknown): string | undefined {
299
- if (Array.isArray(style)) {
300
- return style.reduce<string | undefined>((result, styleItem) => {
301
- if (result) return result;
302
- return getBorderColor(styleItem);
303
- }, undefined);
304
- }
305
- if (style && typeof style === 'object') {
306
- const flattened = StyleSheet.flatten(style as StyleProp<ViewStyle>);
307
- return flattened?.borderColor as string | undefined;
308
- }
309
- return undefined;
310
- }
311
-
312
- // User should see disabled styling (muted border color)
313
- const borderColor = getBorderColor(borderElement.props.style);
314
- expect(borderColor).toBe('#bfc1c5');
315
-
316
- expect(toJSON()).toMatchSnapshot();
317
- });
318
- });
319
-
320
- describe('when user sees an error state', () => {
321
- it('should display error message to help user understand the issue', () => {
322
- const { toJSON, getByText } = renderWithTheme(
323
- <TextInput
324
- label="Amount (AUD)"
325
- prefix="dollar-sign"
326
- required
327
- error="This field is required"
328
- />
329
- );
330
-
331
- // User should see both the label and error message
332
- expect(getByText('This field is required')).toBeTruthy();
333
-
334
- expect(toJSON()).toMatchSnapshot();
335
- });
336
- });
337
-
338
- describe('when user sees helper text', () => {
339
- it('should display guidance text to assist user understanding', () => {
340
- const { toJSON, getByText } = renderWithTheme(
341
- <TextInput
342
- label="Amount (AUD)"
343
- prefix="dollar-sign"
344
- required
345
- helpText="This is helper text"
346
- />
347
- );
348
-
349
- // User should see both the label and helpful guidance
350
- expect(getByText('This is helper text')).toBeTruthy();
351
-
352
- expect(toJSON()).toMatchSnapshot();
353
- });
354
- });
355
-
356
- describe('when user interacts with placeholder text', () => {
357
- describe('starting from empty field', () => {
358
- it('should show placeholder when user focuses, hide when user leaves empty', async () => {
359
- const { getByTestId, queryByPlaceholderText, toJSON } = renderWithTheme(
360
- <TextInput
361
- label="Amount (AUD)"
362
- prefix="dollar-sign"
363
- required
364
- helpText="This is helper text"
365
- placeholder="Enter Amount"
366
- />
367
- );
368
-
369
- const textInput = getByTestId('text-input');
370
- expect(textInput).toBeTruthy();
371
-
372
- // User focuses the input field
373
- fireEvent(textInput, 'focus');
374
- await waitFor(() => {
375
- expect(queryByPlaceholderText('Enter Amount')).toBeTruthy();
376
- });
377
-
378
- // User leaves the field empty and moves focus away
379
- fireEvent(textInput, 'blur');
380
- await waitFor(() => {
381
- expect(textInput).toHaveProp('placeholder', ' ');
382
- });
383
-
384
- // Input remains empty, so placeholder should be hidden again
385
- expect(queryByPlaceholderText('Placeholder')).toBeFalsy();
386
-
387
- expect(toJSON()).toMatchSnapshot();
388
- });
389
- });
390
- });
391
-
392
- describe('when user provides default values', () => {
393
- describe('starting with pre-filled content', () => {
394
- it('should display default value and character count', () => {
395
- const { getByDisplayValue, getByText, toJSON } = renderWithTheme(
396
- <TextInput
397
- label="Amount (AUD)"
398
- prefix="dollar-sign"
399
- required
400
- helpText="This is helper text"
401
- placeholder="Enter Amount"
402
- defaultValue="1000"
403
- maxLength={255}
404
- />
405
- );
406
-
407
- // User should see the pre-filled value
408
- expect(getByDisplayValue('1000')).toBeTruthy();
409
-
410
- // User should see character count reflecting the default value
411
- expect(getByText('4/255')).toBeTruthy();
412
-
413
- expect(toJSON()).toMatchSnapshot();
414
- });
415
- });
416
-
417
- describe('when both default and controlled values are provided', () => {
418
- it('should prioritize controlled value over default value', () => {
419
- const { getByDisplayValue, queryByDisplayValue, getByText, toJSON } =
420
- renderWithTheme(
421
- <TextInput
422
- label="Amount (AUD)"
423
- prefix="dollar-sign"
424
- required
425
- helpText="This is helper text"
426
- placeholder="Enter Amount"
427
- defaultValue="1000"
428
- value="2000"
429
- maxLength={255}
430
- />
431
- );
432
-
433
- // User should see the controlled value, not the default
434
- expect(getByDisplayValue('2000')).toBeVisible();
435
- expect(queryByDisplayValue('1000')).toBeFalsy();
436
-
437
- // Character count should reflect the actual displayed value
438
- expect(getByText('4/255')).toBeTruthy();
439
-
440
- expect(toJSON()).toMatchSnapshot();
441
- });
442
- });
443
- });
444
-
445
- describe('when user applies custom styling', () => {
446
- it('should respect user-provided background color styling', () => {
447
- const { getByTestId, toJSON } = renderWithTheme(
448
- <TextInput
449
- label="Amount (AUD)"
450
- prefix="dollar-sign"
451
- required
452
- helpText="This is helper text"
453
- placeholder="Enter Amount"
454
- defaultValue="1000"
455
- value="2000"
456
- maxLength={255}
457
- style={{ backgroundColor: 'customColor' }}
458
- />
459
- );
460
-
461
- const textInput = getByTestId('text-input');
462
-
463
- // Helper function to extract background color from complex React Native styles
464
- function getBackgroundColor(
465
- style: StyleProp<ViewStyle> | unknown[]
466
- ): string | undefined {
467
- function flatten(s: StyleProp<ViewStyle> | unknown[]): ViewStyle[] {
468
- if (Array.isArray(s)) {
469
- return s
470
- .filter(Boolean)
471
- .reduce<ViewStyle[]>(
472
- (acc, item) =>
473
- acc.concat(flatten(item as StyleProp<ViewStyle> | unknown[])),
474
- []
475
- );
476
- }
477
- if (s) {
478
- return [StyleSheet.flatten(s as StyleProp<ViewStyle>) as ViewStyle];
479
- }
480
- return [];
481
- }
482
- const flat = flatten(style);
483
- for (let i = flat.length - 1; i >= 0; i -= 1) {
484
- if (flat[i].backgroundColor !== undefined) {
485
- return flat[i].backgroundColor as string;
486
- }
487
- }
488
- return undefined;
489
- }
490
-
491
- // User should see their custom background color applied
492
- const textInputStyle = textInput.props.style;
493
- expect(getBackgroundColor(textInputStyle)).toBe('customColor');
494
-
495
- const borderStyle = getByTestId('text-input-border').props.style;
496
- expect(getBackgroundColor(borderStyle)).toBe('customColor');
497
-
498
- expect(toJSON()).toMatchSnapshot();
499
- });
500
- });
501
-
502
- describe('when user needs programmatic control', () => {
503
- it('should provide ref methods for external control of the input', () => {
504
- const ref = React.createRef<
505
- TextInputHandles & {
506
- getNativeTextInputRef(): RNTextInput;
507
- }
508
- >();
509
-
510
- const { getByDisplayValue, toJSON } = renderWithTheme(
511
- <TextInput label="Amount (AUD)" value="2000" ref={ref} />
512
- );
513
-
514
- // User should see the initial value
515
- expect(getByDisplayValue('2000')).toBeTruthy();
516
-
517
- // External code should be able to control the input programmatically
518
- const nativeTextInputRef = ref.current?.getNativeTextInputRef();
519
- if (nativeTextInputRef && ref.current) {
520
- const focusSpy = jest.spyOn(nativeTextInputRef, 'focus');
521
- const clearSpy = jest.spyOn(nativeTextInputRef, 'clear');
522
- const blurSpy = jest.spyOn(nativeTextInputRef, 'blur');
523
- const setNativePropsSpy = jest.spyOn(
524
- nativeTextInputRef,
525
- 'setNativeProps'
526
- );
527
-
528
- act(() => {
529
- ref.current?.focus();
530
- ref.current?.clear();
531
- ref.current?.setNativeProps({ text: '1000' });
532
- ref.current?.blur();
533
- });
534
-
535
- // Verify that programmatic methods work as expected
536
- expect(focusSpy).toHaveBeenCalledTimes(1);
537
- expect(clearSpy).toHaveBeenCalledTimes(1);
538
- expect(setNativePropsSpy).toHaveBeenCalledTimes(1);
539
- expect(blurSpy).toHaveBeenCalledTimes(1);
540
- }
541
-
542
- expect(toJSON()).toMatchSnapshot();
543
- });
544
- });
545
-
546
- describe('when user chooses textarea variant', () => {
547
- it('should provide multiline text input experience', () => {
548
- const { toJSON, getByDisplayValue } = renderWithTheme(
549
- <TextInput label="Amount (AUD)" value="2000" variant="textarea" />
550
- );
551
-
552
- // User should see their entered value
553
- expect(getByDisplayValue('2000')).toBeTruthy();
554
-
555
- expect(toJSON()).toMatchSnapshot();
556
- });
557
- });
558
-
559
- describe('when user encounters an error state that needs to be corrected', () => {
560
- it('should allow focusing input even when error is present', () => {
561
- const onFocus = jest.fn();
562
- const { getByTestId } = renderWithTheme(
563
- <TextInput label="Email" error="Invalid Email" onFocus={onFocus} />
564
- );
565
-
566
- // User should be able to focus the input despite the error
567
- const textInput = getByTestId('text-input');
568
- fireEvent(textInput, 'focus');
569
-
570
- // Should trigger focus callback
571
- expect(onFocus).toHaveBeenCalled();
572
- });
573
-
574
- it('should display focused styling when error input is focused', async () => {
575
- const { getByTestId } = renderWithTheme(
576
- <TextInput label="Email" error="Invalid Email" />
577
- );
578
-
579
- // Focus the input
580
- const textInput = getByTestId('text-input');
581
- fireEvent(textInput, 'focus');
582
-
583
- await waitFor(() => {
584
- const border = getByTestId('text-input-border');
585
- expect(border).toHaveProp('themeFocused', true);
586
- expect(border).toHaveProp('themeState', 'error');
587
- });
588
- });
589
- });
590
-
591
- describe('when text is changed in the input', () => {
592
- it('updates the display value', () => {
593
- const ControllableInput = () => {
594
- const [value, setValue] = React.useState('');
595
- return (
596
- <TextInput
597
- label="Amount (AUD)"
598
- value={value}
599
- onChangeText={setValue}
600
- />
601
- );
602
- };
603
- const { getByTestId, getByDisplayValue } = renderWithTheme(
604
- <ControllableInput />
605
- );
606
-
607
- fireEvent.changeText(getByTestId('text-input'), '100');
608
- expect(getByDisplayValue('100')).toBeTruthy();
609
- });
610
-
611
- it('does not update when not editable', () => {
612
- const ControllableInput = () => {
613
- const [value, setValue] = React.useState('hello');
614
- return (
615
- <TextInput
616
- label="Amount (AUD)"
617
- value={value}
618
- onChangeText={setValue}
619
- editable={false}
620
- />
621
- );
622
- };
623
- const { getByTestId, getByDisplayValue } = renderWithTheme(
624
- <ControllableInput />
625
- );
626
-
627
- // This should not work
628
- fireEvent.changeText(getByTestId('text-input'), 'new value');
629
-
630
- // show old value
631
- expect(getByDisplayValue('hello')).toBeTruthy();
632
- });
633
-
634
- it('allows user to press label to focus and type', () => {
635
- const ControllableInput = () => {
636
- const [value, setValue] = React.useState('');
637
- return (
638
- <TextInput label="My Label" value={value} onChangeText={setValue} />
639
- );
640
- };
641
- const { getByText, getByDisplayValue, getByTestId } = renderWithTheme(
642
- <ControllableInput />
643
- );
644
-
645
- // Pressing the label should focus the input
646
- fireEvent.press(getByText('My Label'));
647
-
648
- // Now we can type into the focused input
649
- fireEvent.changeText(getByTestId('text-input'), 'hello');
650
-
651
- // Verify the value has changed
652
- expect(getByDisplayValue('hello')).toBeVisible();
653
- });
654
- });
655
-
656
- it('should allow user to type text', () => {
657
- const handleChangeText = jest.fn();
658
- const { getByTestId } = renderWithTheme(
659
- <TextInput
660
- label="Email"
661
- onChangeText={handleChangeText}
662
- testID="email-input-container"
663
- />
664
- );
665
-
666
- // 1. Get both the container and the actual input field
667
- const container = getByTestId('email-input-container');
668
- const textInput = getByTestId('text-input-email-input-container');
669
-
670
- // 2. Focus the component by interacting with the container
671
- fireEvent(container, 'focus');
672
-
673
- // 3. Type text by firing changeText on the input field itself
674
- fireEvent.changeText(textInput, 'hello world');
675
-
676
- // 4. Assert that your onChangeText handler was called with the correct text
677
- expect(handleChangeText).toHaveBeenCalledWith('hello world');
678
- });
679
- });