ferns-ui 1.9.1 → 1.11.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 (73) hide show
  1. package/dist/Accordion.js +1 -1
  2. package/dist/Accordion.js.map +1 -1
  3. package/dist/Accordion.test.d.ts +1 -0
  4. package/dist/Accordion.test.js +71 -0
  5. package/dist/Accordion.test.js.map +1 -0
  6. package/dist/AddressField.test.d.ts +1 -0
  7. package/dist/AddressField.test.js +65 -0
  8. package/dist/AddressField.test.js.map +1 -0
  9. package/dist/Avatar.js +2 -2
  10. package/dist/Avatar.js.map +1 -1
  11. package/dist/Avatar.test.d.ts +1 -0
  12. package/dist/Avatar.test.js +131 -0
  13. package/dist/Avatar.test.js.map +1 -0
  14. package/dist/Badge.d.ts +1 -1
  15. package/dist/Badge.js +3 -3
  16. package/dist/Badge.js.map +1 -1
  17. package/dist/Badge.test.d.ts +1 -0
  18. package/dist/Badge.test.js +76 -0
  19. package/dist/Badge.test.js.map +1 -0
  20. package/dist/Box.test.d.ts +1 -0
  21. package/dist/Box.test.js +528 -0
  22. package/dist/Box.test.js.map +1 -0
  23. package/dist/Common.d.ts +100 -1
  24. package/dist/Common.js.map +1 -1
  25. package/dist/DateTimeField.js +15 -2
  26. package/dist/DateTimeField.js.map +1 -1
  27. package/dist/InfoModalIcon.js +1 -1
  28. package/dist/InfoModalIcon.js.map +1 -1
  29. package/dist/SectionDivider.d.ts +2 -0
  30. package/dist/SectionDivider.js +12 -0
  31. package/dist/SectionDivider.js.map +1 -0
  32. package/dist/Slider.d.ts +3 -0
  33. package/dist/Slider.js +94 -0
  34. package/dist/Slider.js.map +1 -0
  35. package/dist/Text.js +2 -0
  36. package/dist/Text.js.map +1 -1
  37. package/dist/TextField.test.js +9 -9
  38. package/dist/TextField.test.js.map +1 -1
  39. package/dist/Tooltip.js +2 -0
  40. package/dist/Tooltip.js.map +1 -1
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +2 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/setupTests.js +40 -2
  45. package/dist/setupTests.js.map +1 -1
  46. package/dist/test-utils.js.map +1 -1
  47. package/package.json +2 -1
  48. package/src/Accordion.test.tsx +104 -0
  49. package/src/Accordion.tsx +1 -0
  50. package/src/AddressField.test.tsx +89 -0
  51. package/src/Avatar.test.tsx +163 -0
  52. package/src/Avatar.tsx +2 -0
  53. package/src/Badge.test.tsx +116 -0
  54. package/src/Badge.tsx +3 -1
  55. package/src/Box.test.tsx +665 -0
  56. package/src/Common.ts +114 -1
  57. package/src/DateTimeField.tsx +15 -2
  58. package/src/InfoModalIcon.tsx +1 -0
  59. package/src/SectionDivider.tsx +18 -0
  60. package/src/Slider.tsx +205 -0
  61. package/src/Text.tsx +2 -0
  62. package/src/TextField.test.tsx +59 -71
  63. package/src/Tooltip.tsx +2 -0
  64. package/src/__snapshots__/Accordion.test.tsx.snap +120 -0
  65. package/src/__snapshots__/AddressField.test.tsx.snap +464 -0
  66. package/src/__snapshots__/Avatar.test.tsx.snap +78 -0
  67. package/src/__snapshots__/Badge.test.tsx.snap +44 -0
  68. package/src/__snapshots__/Box.test.tsx.snap +159 -0
  69. package/src/__snapshots__/TextArea.test.tsx.snap +12 -0
  70. package/src/__snapshots__/TextField.test.tsx.snap +38 -1
  71. package/src/index.tsx +2 -0
  72. package/src/setupTests.ts +45 -2
  73. package/src/test-utils.tsx +1 -0
@@ -1,10 +1,8 @@
1
1
  import {act, userEvent} from "@testing-library/react-native";
2
2
  import React from "react";
3
3
 
4
- import {TextField} from "./TextField";
5
4
  import {renderWithTheme} from "./test-utils";
6
-
7
-
5
+ import {TextField} from "./TextField";
8
6
 
9
7
  describe("TextField", () => {
10
8
  let mockOnChange: jest.Mock;
@@ -30,7 +28,7 @@ describe("TextField", () => {
30
28
  const {getByDisplayValue} = renderWithTheme(
31
29
  <TextField value="test value" onChange={mockOnChange} />
32
30
  );
33
-
31
+
34
32
  expect(getByDisplayValue("test value").props.value).toBe("test value");
35
33
  });
36
34
 
@@ -38,7 +36,7 @@ describe("TextField", () => {
38
36
  const {getByText} = renderWithTheme(
39
37
  <TextField title="Test Title" value="" onChange={mockOnChange} />
40
38
  );
41
-
39
+
42
40
  expect(getByText("Test Title")).toBeTruthy();
43
41
  });
44
42
 
@@ -46,7 +44,7 @@ describe("TextField", () => {
46
44
  const {getByPlaceholderText} = renderWithTheme(
47
45
  <TextField placeholder="Enter text" value="" onChange={mockOnChange} />
48
46
  );
49
-
47
+
50
48
  expect(getByPlaceholderText("Enter text")).toBeTruthy();
51
49
  });
52
50
 
@@ -54,7 +52,7 @@ describe("TextField", () => {
54
52
  const {getByText} = renderWithTheme(
55
53
  <TextField helperText="This is helper text" value="" onChange={mockOnChange} />
56
54
  );
57
-
55
+
58
56
  expect(getByText("This is helper text")).toBeTruthy();
59
57
  });
60
58
 
@@ -62,7 +60,7 @@ describe("TextField", () => {
62
60
  const {getByText} = renderWithTheme(
63
61
  <TextField errorText="This is an error" value="" onChange={mockOnChange} />
64
62
  );
65
-
63
+
66
64
  expect(getByText("This is an error")).toBeTruthy();
67
65
  });
68
66
  });
@@ -70,9 +68,7 @@ describe("TextField", () => {
70
68
  describe("user interactions", () => {
71
69
  it("should call onChange when text is entered", async () => {
72
70
  const user = userEvent.setup();
73
- const {getByDisplayValue} = renderWithTheme(
74
- <TextField value="" onChange={mockOnChange} />
75
- );
71
+ const {getByDisplayValue} = renderWithTheme(<TextField value="" onChange={mockOnChange} />);
76
72
 
77
73
  const input = getByDisplayValue("");
78
74
  await user.type(input, "hello");
@@ -93,7 +89,7 @@ describe("TextField", () => {
93
89
  it("should call onBlur when input loses focus", async () => {
94
90
  const user = userEvent.setup();
95
91
  const {getByDisplayValue} = renderWithTheme(
96
- <TextField value="test" onChange={mockOnChange} onBlur={mockOnBlur} />
92
+ <TextField value="test" onBlur={mockOnBlur} onChange={mockOnChange} />
97
93
  );
98
94
 
99
95
  const input = getByDisplayValue("test");
@@ -126,7 +122,7 @@ describe("TextField", () => {
126
122
  const {getByDisplayValue} = renderWithTheme(
127
123
  <TextField type="email" value="" onChange={mockOnChange} />
128
124
  );
129
-
125
+
130
126
  const input = getByDisplayValue("");
131
127
  expect(input.props.keyboardType).toBe("email-address");
132
128
  });
@@ -135,7 +131,7 @@ describe("TextField", () => {
135
131
  const {getByDisplayValue} = renderWithTheme(
136
132
  <TextField type="password" value="" onChange={mockOnChange} />
137
133
  );
138
-
134
+
139
135
  const input = getByDisplayValue("");
140
136
  expect(input.props.secureTextEntry).toBe(true);
141
137
  });
@@ -144,16 +140,18 @@ describe("TextField", () => {
144
140
  const {getByDisplayValue} = renderWithTheme(
145
141
  <TextField type="url" value="" onChange={mockOnChange} />
146
142
  );
147
-
143
+
148
144
  const input = getByDisplayValue("");
149
- expect(input.props.keyboardType === "url" || input.props.keyboardType === "default").toBe(true);
145
+ expect(input.props.keyboardType === "url" || input.props.keyboardType === "default").toBe(
146
+ true
147
+ );
150
148
  });
151
149
 
152
150
  it("should render phoneNumber type with number keyboard", () => {
153
151
  const {getByDisplayValue} = renderWithTheme(
154
152
  <TextField type="phoneNumber" value="" onChange={mockOnChange} />
155
153
  );
156
-
154
+
157
155
  const input = getByDisplayValue("");
158
156
  expect(input.props.keyboardType).toBe("number-pad");
159
157
  });
@@ -162,7 +160,7 @@ describe("TextField", () => {
162
160
  const {getByDisplayValue} = renderWithTheme(
163
161
  <TextField type="search" value="" onChange={mockOnChange} />
164
162
  );
165
-
163
+
166
164
  const input = getByDisplayValue("");
167
165
  expect(input.props.keyboardType).toBe("default");
168
166
  });
@@ -173,7 +171,7 @@ describe("TextField", () => {
173
171
  const {getByDisplayValue} = renderWithTheme(
174
172
  <TextField multiline value="" onChange={mockOnChange} />
175
173
  );
176
-
174
+
177
175
  const input = getByDisplayValue("");
178
176
  expect(input.props.multiline).toBe(true);
179
177
  });
@@ -182,16 +180,16 @@ describe("TextField", () => {
182
180
  const {getByDisplayValue} = renderWithTheme(
183
181
  <TextField multiline rows={5} value="" onChange={mockOnChange} />
184
182
  );
185
-
183
+
186
184
  const input = getByDisplayValue("");
187
185
  expect(input.props.numberOfLines).toBe(5);
188
186
  });
189
187
 
190
188
  it("should handle grow behavior with multiline", () => {
191
189
  const {getByDisplayValue} = renderWithTheme(
192
- <TextField multiline grow value="" onChange={mockOnChange} />
190
+ <TextField grow multiline value="" onChange={mockOnChange} />
193
191
  );
194
-
192
+
195
193
  const input = getByDisplayValue("");
196
194
  expect(input.props.multiline).toBe(true);
197
195
  });
@@ -202,7 +200,7 @@ describe("TextField", () => {
202
200
  const {getByDisplayValue} = renderWithTheme(
203
201
  <TextField disabled value="test" onChange={mockOnChange} />
204
202
  );
205
-
203
+
206
204
  const input = getByDisplayValue("test");
207
205
  expect(input.props.readOnly).toBe(true);
208
206
  });
@@ -219,7 +217,7 @@ describe("TextField", () => {
219
217
 
220
218
  it("should not call onBlur when disabled", async () => {
221
219
  const {getByDisplayValue} = renderWithTheme(
222
- <TextField disabled value="test" onChange={mockOnChange} onBlur={mockOnBlur} />
220
+ <TextField disabled value="test" onBlur={mockOnBlur} onChange={mockOnChange} />
223
221
  );
224
222
 
225
223
  const input = getByDisplayValue("test");
@@ -236,7 +234,7 @@ describe("TextField", () => {
236
234
  const {getByDisplayValue} = renderWithTheme(
237
235
  <TextField iconName="check" value="" onChange={mockOnChange} />
238
236
  );
239
-
237
+
240
238
  const input = getByDisplayValue("");
241
239
  expect(input).toBeTruthy();
242
240
  });
@@ -244,7 +242,12 @@ describe("TextField", () => {
244
242
  it("should call onIconClick when icon is pressed", async () => {
245
243
  const mockOnIconClick = jest.fn();
246
244
  const {getByDisplayValue} = renderWithTheme(
247
- <TextField iconName="check" onIconClick={mockOnIconClick} value="" onChange={mockOnChange} />
245
+ <TextField
246
+ iconName="check"
247
+ value=""
248
+ onChange={mockOnChange}
249
+ onIconClick={mockOnIconClick}
250
+ />
248
251
  );
249
252
 
250
253
  const input = getByDisplayValue("");
@@ -254,10 +257,8 @@ describe("TextField", () => {
254
257
 
255
258
  describe("accessibility", () => {
256
259
  it("should have correct accessibility properties", () => {
257
- const {getByDisplayValue} = renderWithTheme(
258
- <TextField value="" onChange={mockOnChange} />
259
- );
260
-
260
+ const {getByDisplayValue} = renderWithTheme(<TextField value="" onChange={mockOnChange} />);
261
+
261
262
  const input = getByDisplayValue("");
262
263
  expect(input.props.accessibilityHint).toBe("Enter text here");
263
264
  expect(input.props["aria-label"]).toBe("Text input field");
@@ -267,7 +268,7 @@ describe("TextField", () => {
267
268
  const {getByDisplayValue} = renderWithTheme(
268
269
  <TextField disabled value="" onChange={mockOnChange} />
269
270
  );
270
-
271
+
271
272
  const input = getByDisplayValue("");
272
273
  expect(input.props.accessibilityState.disabled).toBe(true);
273
274
  });
@@ -278,7 +279,7 @@ describe("TextField", () => {
278
279
  const {getByDisplayValue} = renderWithTheme(
279
280
  <TextField autoComplete="username" value="" onChange={mockOnChange} />
280
281
  );
281
-
282
+
282
283
  const input = getByDisplayValue("");
283
284
  expect(input).toBeTruthy();
284
285
  });
@@ -287,7 +288,7 @@ describe("TextField", () => {
287
288
  const {getByDisplayValue} = renderWithTheme(
288
289
  <TextField type="email" value="" onChange={mockOnChange} />
289
290
  );
290
-
291
+
291
292
  const input = getByDisplayValue("");
292
293
  expect(input.props.textContentType).toBe("emailAddress");
293
294
  });
@@ -296,7 +297,7 @@ describe("TextField", () => {
296
297
  const {getByDisplayValue} = renderWithTheme(
297
298
  <TextField type="password" value="" onChange={mockOnChange} />
298
299
  );
299
-
300
+
300
301
  const input = getByDisplayValue("");
301
302
  expect(input.props.textContentType).toBe("password");
302
303
  });
@@ -304,19 +305,15 @@ describe("TextField", () => {
304
305
 
305
306
  describe("edge cases", () => {
306
307
  it("should handle empty value", () => {
307
- const {getByDisplayValue} = renderWithTheme(
308
- <TextField value="" onChange={mockOnChange} />
309
- );
310
-
308
+ const {getByDisplayValue} = renderWithTheme(<TextField value="" onChange={mockOnChange} />);
309
+
311
310
  const input = getByDisplayValue("");
312
311
  expect(input.props.value).toBe("");
313
312
  });
314
313
 
315
314
  it("should handle undefined value", () => {
316
- const {root} = renderWithTheme(
317
- <TextField value={undefined} onChange={mockOnChange} />
318
- );
319
-
315
+ const {root} = renderWithTheme(<TextField value={undefined} onChange={mockOnChange} />);
316
+
320
317
  expect(root).toBeTruthy();
321
318
  });
322
319
 
@@ -325,7 +322,7 @@ describe("TextField", () => {
325
322
  const {getByDisplayValue} = renderWithTheme(
326
323
  <TextField value={longText} onChange={mockOnChange} />
327
324
  );
328
-
325
+
329
326
  const input = getByDisplayValue(longText);
330
327
  expect(input.props.value).toBe(longText);
331
328
  });
@@ -335,7 +332,7 @@ describe("TextField", () => {
335
332
  const {getByDisplayValue} = renderWithTheme(
336
333
  <TextField value={specialText} onChange={mockOnChange} />
337
334
  );
338
-
335
+
339
336
  const input = getByDisplayValue(specialText);
340
337
  expect(input.props.value).toBe(specialText);
341
338
  });
@@ -346,7 +343,7 @@ describe("TextField", () => {
346
343
  const {getByDisplayValue} = renderWithTheme(
347
344
  <TextField returnKeyType="done" value="" onChange={mockOnChange} />
348
345
  );
349
-
346
+
350
347
  const input = getByDisplayValue("");
351
348
  expect(input.props.enterKeyHint).toBe("done");
352
349
  });
@@ -355,7 +352,7 @@ describe("TextField", () => {
355
352
  const {getByDisplayValue} = renderWithTheme(
356
353
  <TextField blurOnSubmit={false} value="" onChange={mockOnChange} />
357
354
  );
358
-
355
+
359
356
  const input = getByDisplayValue("");
360
357
  expect(input.props.blurOnSubmit).toBe(false);
361
358
  });
@@ -364,39 +361,35 @@ describe("TextField", () => {
364
361
  describe("input ref", () => {
365
362
  it("should call inputRef with the input reference", () => {
366
363
  const mockInputRef = jest.fn();
367
- renderWithTheme(
368
- <TextField inputRef={mockInputRef} value="" onChange={mockOnChange} />
369
- );
370
-
364
+ renderWithTheme(<TextField inputRef={mockInputRef} value="" onChange={mockOnChange} />);
365
+
371
366
  expect(mockInputRef).toHaveBeenCalled();
372
367
  });
373
368
  });
374
369
 
375
370
  describe("snapshots", () => {
376
371
  it("should match snapshot with default props", () => {
377
- const component = renderWithTheme(
378
- <TextField value="test value" onChange={mockOnChange} />
379
- );
372
+ const component = renderWithTheme(<TextField value="test value" onChange={mockOnChange} />);
380
373
  expect(component.toJSON()).toMatchSnapshot();
381
374
  });
382
375
 
383
376
  it("should match snapshot with all props", () => {
384
377
  const component = renderWithTheme(
385
378
  <TextField
386
- title="Test Title"
387
- placeholder="Enter text"
388
- helperText="Helper text"
379
+ disabled={false}
389
380
  errorText="Error text"
381
+ helperText="Helper text"
390
382
  iconName="check"
391
- onIconClick={jest.fn()}
383
+ multiline={false}
384
+ placeholder="Enter text"
385
+ title="Test Title"
386
+ type="text"
392
387
  value="test value"
393
- onChange={mockOnChange}
394
- onFocus={mockOnFocus}
395
388
  onBlur={mockOnBlur}
389
+ onChange={mockOnChange}
396
390
  onEnter={mockOnEnter}
397
- disabled={false}
398
- multiline={false}
399
- type="text"
391
+ onFocus={mockOnFocus}
392
+ onIconClick={jest.fn()}
400
393
  />
401
394
  );
402
395
  expect(component.toJSON()).toMatchSnapshot();
@@ -404,12 +397,7 @@ describe("TextField", () => {
404
397
 
405
398
  it("should match snapshot when disabled", () => {
406
399
  const component = renderWithTheme(
407
- <TextField
408
- title="Disabled Field"
409
- value="disabled value"
410
- onChange={mockOnChange}
411
- disabled={true}
412
- />
400
+ <TextField disabled title="Disabled Field" value="disabled value" onChange={mockOnChange} />
413
401
  );
414
402
  expect(component.toJSON()).toMatchSnapshot();
415
403
  });
@@ -417,11 +405,11 @@ describe("TextField", () => {
417
405
  it("should match snapshot with multiline", () => {
418
406
  const component = renderWithTheme(
419
407
  <TextField
408
+ multiline
409
+ rows={3}
420
410
  title="Multiline Field"
421
411
  value="line 1\nline 2"
422
412
  onChange={mockOnChange}
423
- multiline={true}
424
- rows={3}
425
413
  />
426
414
  );
427
415
  expect(component.toJSON()).toMatchSnapshot();
@@ -430,10 +418,10 @@ describe("TextField", () => {
430
418
  it("should match snapshot with error state", () => {
431
419
  const component = renderWithTheme(
432
420
  <TextField
421
+ errorText="This field is required"
433
422
  title="Error Field"
434
423
  value=""
435
424
  onChange={mockOnChange}
436
- errorText="This field is required"
437
425
  />
438
426
  );
439
427
  expect(component.toJSON()).toMatchSnapshot();
package/src/Tooltip.tsx CHANGED
@@ -192,6 +192,8 @@ export const Tooltip: FC<TooltipProps> = ({text, children, idealPosition, includ
192
192
  if (hideTooltipTimer.current) {
193
193
  clearTimeout(hideTooltipTimer.current);
194
194
  }
195
+ // Hide tooltip on unmount to prevent it from staying stuck on screen
196
+ setVisible(false);
195
197
  };
196
198
  }, []);
197
199
 
@@ -0,0 +1,120 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Accordion renders correctly with default props 1`] = `
4
+ <View
5
+ style={
6
+ {
7
+ "borderBottomColor": "#CDCDCD",
8
+ "borderBottomWidth": 2,
9
+ "borderTopColor": "#CDCDCD",
10
+ "borderTopWidth": 2,
11
+ "padding": 16,
12
+ "width": "100%",
13
+ }
14
+ }
15
+ >
16
+ <View
17
+ style={
18
+ {
19
+ "alignItems": "center",
20
+ "flexDirection": "row",
21
+ "justifyContent": "space-between",
22
+ }
23
+ }
24
+ >
25
+ <View
26
+ style={
27
+ {
28
+ "flexDirection": "column",
29
+ "gap": 4,
30
+ }
31
+ }
32
+ >
33
+ <View
34
+ style={
35
+ {
36
+ "alignItems": "center",
37
+ "flexDirection": "row",
38
+ }
39
+ }
40
+ >
41
+ <Text
42
+ numberOfLines={0}
43
+ style={
44
+ {
45
+ "color": "#1C1C1C",
46
+ "fontFamily": "heading-bold",
47
+ "fontSize": 16,
48
+ }
49
+ }
50
+ >
51
+ Test Title
52
+ </Text>
53
+ </View>
54
+ </View>
55
+ <View>
56
+ <View
57
+ accessibilityState={
58
+ {
59
+ "busy": undefined,
60
+ "checked": undefined,
61
+ "disabled": undefined,
62
+ "expanded": undefined,
63
+ "selected": undefined,
64
+ }
65
+ }
66
+ accessibilityValue={
67
+ {
68
+ "max": undefined,
69
+ "min": undefined,
70
+ "now": undefined,
71
+ "text": undefined,
72
+ }
73
+ }
74
+ accessible={true}
75
+ aria-role="button"
76
+ collapsable={false}
77
+ focusable={true}
78
+ hitSlop={
79
+ {
80
+ "bottom": 20,
81
+ "left": 20,
82
+ "right": 20,
83
+ "top": 20,
84
+ }
85
+ }
86
+ onBlur={[Function]}
87
+ onClick={[Function]}
88
+ onFocus={[Function]}
89
+ onResponderGrant={[Function]}
90
+ onResponderMove={[Function]}
91
+ onResponderRelease={[Function]}
92
+ onResponderTerminate={[Function]}
93
+ onResponderTerminationRequest={[Function]}
94
+ onStartShouldSetResponder={[Function]}
95
+ testID="accordion-toggle"
96
+ >
97
+ <View
98
+ color="#0A7092"
99
+ name="chevron-up"
100
+ size={16}
101
+ testID="chevron-up"
102
+ />
103
+ </View>
104
+ </View>
105
+ </View>
106
+ <View
107
+ style={
108
+ {
109
+ "marginTop": 8,
110
+ }
111
+ }
112
+ >
113
+ <Text
114
+ testID="test-content"
115
+ >
116
+ Test Content
117
+ </Text>
118
+ </View>
119
+ </View>
120
+ `;