@aws-amplify/ui-react-native 1.2.20 → 1.2.22

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 (113) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/Authenticator/Authenticator.d.ts +101 -148
  3. package/dist/Authenticator/Authenticator.js +2 -3
  4. package/dist/Authenticator/Defaults/ConfirmResetPassword/ConfirmResetPassword.d.ts +13 -2
  5. package/dist/Authenticator/Defaults/ConfirmResetPassword/ConfirmResetPassword.js +4 -3
  6. package/dist/Authenticator/Defaults/ConfirmSignIn/ConfirmSignIn.d.ts +13 -2
  7. package/dist/Authenticator/Defaults/ConfirmSignIn/ConfirmSignIn.js +4 -3
  8. package/dist/Authenticator/Defaults/ConfirmSignUp/ConfirmSignUp.d.ts +13 -2
  9. package/dist/Authenticator/Defaults/ConfirmSignUp/ConfirmSignUp.js +4 -3
  10. package/dist/Authenticator/Defaults/ConfirmVerifyUser/ConfirmVerifyUser.d.ts +13 -2
  11. package/dist/Authenticator/Defaults/ConfirmVerifyUser/ConfirmVerifyUser.js +4 -3
  12. package/dist/Authenticator/Defaults/ForceNewPassword/ForceNewPassword.d.ts +13 -2
  13. package/dist/Authenticator/Defaults/ForceNewPassword/ForceNewPassword.js +4 -3
  14. package/dist/Authenticator/Defaults/ResetPassword/ResetPassword.d.ts +13 -2
  15. package/dist/Authenticator/Defaults/ResetPassword/ResetPassword.js +4 -3
  16. package/dist/Authenticator/Defaults/SetupTOTP/SetupTOTP.d.ts +13 -2
  17. package/dist/Authenticator/Defaults/SetupTOTP/SetupTOTP.js +4 -3
  18. package/dist/Authenticator/Defaults/SignIn/SignIn.d.ts +13 -2
  19. package/dist/Authenticator/Defaults/SignIn/SignIn.js +4 -3
  20. package/dist/Authenticator/Defaults/SignUp/SignUp.d.ts +13 -2
  21. package/dist/Authenticator/Defaults/SignUp/SignUp.js +4 -3
  22. package/dist/Authenticator/Defaults/VerifyUser/VerifyUser.d.ts +13 -2
  23. package/dist/Authenticator/Defaults/VerifyUser/VerifyUser.js +4 -3
  24. package/dist/Authenticator/Defaults/types.d.ts +21 -20
  25. package/dist/Authenticator/common/DefaultContent/styles.js +1 -2
  26. package/dist/Authenticator/common/DefaultContent/types.d.ts +1 -1
  27. package/dist/Authenticator/common/DefaultFormFields/DefaultRadioFormFields.d.ts +7 -3
  28. package/dist/Authenticator/common/DefaultFormFields/DefaultRadioFormFields.js +4 -3
  29. package/dist/Authenticator/common/DefaultFormFields/DefaultTextFormFields.d.ts +6 -2
  30. package/dist/Authenticator/common/DefaultFormFields/DefaultTextFormFields.js +3 -3
  31. package/dist/Authenticator/common/DefaultFormFields/types.d.ts +12 -3
  32. package/dist/Authenticator/hooks/types.d.ts +3 -2
  33. package/dist/Authenticator/hooks/useFieldValues/types.d.ts +4 -1
  34. package/dist/Authenticator/hooks/useFieldValues/useFieldValues.d.ts +1 -1
  35. package/dist/Authenticator/hooks/useFieldValues/useFieldValues.js +21 -3
  36. package/dist/Authenticator/hooks/useFieldValues/utils.d.ts +10 -1
  37. package/dist/Authenticator/hooks/useFieldValues/utils.js +32 -2
  38. package/dist/primitives/Heading/styles.js +5 -5
  39. package/dist/primitives/Label/styles.js +2 -2
  40. package/dist/primitives/TextField/TextField.js +2 -1
  41. package/dist/primitives/TextField/styles.js +6 -3
  42. package/dist/primitives/TextField/types.d.ts +1 -0
  43. package/dist/theme/createTheme.js +24 -18
  44. package/dist/theme/types.d.ts +1 -1
  45. package/dist/version.d.ts +1 -1
  46. package/dist/version.js +1 -1
  47. package/jest.config.js +1 -0
  48. package/package.json +5 -5
  49. package/src/Authenticator/Authenticator.tsx +2 -6
  50. package/src/Authenticator/Defaults/ConfirmResetPassword/ConfirmResetPassword.tsx +7 -3
  51. package/src/Authenticator/Defaults/ConfirmResetPassword/__tests__/__snapshots__/ConfirmResetPassword.spec.tsx.snap +34 -30
  52. package/src/Authenticator/Defaults/ConfirmSignIn/ConfirmSignIn.tsx +7 -3
  53. package/src/Authenticator/Defaults/ConfirmSignIn/__tests__/ConfirmSignIn.spec.tsx +1 -0
  54. package/src/Authenticator/Defaults/ConfirmSignIn/__tests__/__snapshots__/ConfirmSignIn.spec.tsx.snap +14 -14
  55. package/src/Authenticator/Defaults/ConfirmSignUp/ConfirmSignUp.tsx +7 -3
  56. package/src/Authenticator/Defaults/ConfirmSignUp/__tests__/ConfirmSignUp.spec.tsx +1 -0
  57. package/src/Authenticator/Defaults/ConfirmSignUp/__tests__/__snapshots__/ConfirmSignUp.spec.tsx.snap +12 -15
  58. package/src/Authenticator/Defaults/ConfirmVerifyUser/ConfirmVerifyUser.tsx +7 -3
  59. package/src/Authenticator/Defaults/ConfirmVerifyUser/__tests__/ConfirmVerifyUser.spec.tsx +1 -0
  60. package/src/Authenticator/Defaults/ConfirmVerifyUser/__tests__/__snapshots__/ConfirmVerifyUser.spec.tsx.snap +6 -9
  61. package/src/Authenticator/Defaults/ForceNewPassword/ForceNewPassword.tsx +7 -3
  62. package/src/Authenticator/Defaults/ForceNewPassword/__tests__/__snapshots__/ForceNewPassword.spec.tsx.snap +13 -10
  63. package/src/Authenticator/Defaults/ResetPassword/ResetPassword.tsx +7 -3
  64. package/src/Authenticator/Defaults/ResetPassword/__tests__/ResetPassword.spec.tsx +1 -0
  65. package/src/Authenticator/Defaults/ResetPassword/__tests__/__snapshots__/ResetPassword.spec.tsx.snap +14 -14
  66. package/src/Authenticator/Defaults/SetupTOTP/SetupTOTP.tsx +7 -3
  67. package/src/Authenticator/Defaults/SetupTOTP/__tests__/SetupTOTP.spec.tsx +1 -0
  68. package/src/Authenticator/Defaults/SetupTOTP/__tests__/__snapshots__/SetupTOTP.spec.tsx.snap +22 -22
  69. package/src/Authenticator/Defaults/SignIn/SignIn.tsx +7 -3
  70. package/src/Authenticator/Defaults/SignIn/__tests__/SignIn.spec.tsx +1 -0
  71. package/src/Authenticator/Defaults/SignIn/__tests__/__snapshots__/SignIn.spec.tsx.snap +36 -33
  72. package/src/Authenticator/Defaults/SignUp/SignUp.tsx +7 -3
  73. package/src/Authenticator/Defaults/SignUp/__tests__/__snapshots__/SignUp.spec.tsx.snap +111 -96
  74. package/src/Authenticator/Defaults/VerifyUser/VerifyUser.tsx +7 -3
  75. package/src/Authenticator/Defaults/VerifyUser/__tests__/VerifyUser.spec.tsx +1 -0
  76. package/src/Authenticator/Defaults/VerifyUser/__tests__/__snapshots__/VerifyUser.spec.tsx.snap +16 -18
  77. package/src/Authenticator/Defaults/types.ts +63 -49
  78. package/src/Authenticator/__tests__/Authenticator.spec.tsx +16 -19
  79. package/src/Authenticator/__tests__/__snapshots__/Authenticator.spec.tsx.snap +1 -9
  80. package/src/Authenticator/__tests__/withAuthenticator.spec.tsx +1 -1
  81. package/src/Authenticator/common/DefaultContent/styles.ts +1 -2
  82. package/src/Authenticator/common/DefaultContent/types.ts +1 -4
  83. package/src/Authenticator/common/DefaultFormFields/DefaultRadioFormFields.tsx +8 -6
  84. package/src/Authenticator/common/DefaultFormFields/DefaultTextFormFields.tsx +10 -7
  85. package/src/Authenticator/common/DefaultFormFields/types.ts +15 -5
  86. package/src/Authenticator/common/DefaultHeader/__tests__/__snapshots__/DefaultHeader.spec.tsx.snap +1 -1
  87. package/src/Authenticator/common/FederatedProviderButton/__tests__/__snapshots__/FederatedProviderButton.spec.tsx.snap +4 -4
  88. package/src/Authenticator/common/FederatedProviderButtons/__tests__/__snapshots__/FederatedProviderButtons.spec.tsx.snap +4 -4
  89. package/src/Authenticator/hooks/types.ts +3 -0
  90. package/src/Authenticator/hooks/useFieldValues/__tests__/useFieldValues.spec.ts +75 -2
  91. package/src/Authenticator/hooks/useFieldValues/__tests__/utils.spec.ts +67 -1
  92. package/src/Authenticator/hooks/useFieldValues/types.ts +5 -0
  93. package/src/Authenticator/hooks/useFieldValues/useFieldValues.ts +26 -1
  94. package/src/Authenticator/hooks/useFieldValues/utils.ts +44 -1
  95. package/src/primitives/Checkbox/__tests__/__snapshots__/Checkbox.spec.tsx.snap +14 -14
  96. package/src/primitives/Divider/__tests__/__snapshots__/Divider.spec.tsx.snap +4 -4
  97. package/src/primitives/Heading/__tests__/__snapshots__/Heading.spec.tsx.snap +7 -7
  98. package/src/primitives/Heading/styles.ts +5 -5
  99. package/src/primitives/Label/__tests__/__snapshots__/Label.spec.tsx.snap +8 -8
  100. package/src/primitives/Label/styles.ts +2 -2
  101. package/src/primitives/PasswordField/__tests__/__snapshots__/PasswordField.spec.tsx.snap +25 -20
  102. package/src/primitives/PhoneNumberField/__tests__/__snapshots__/PhoneNumberField.spec.tsx.snap +6 -0
  103. package/src/primitives/Radio/__tests__/__snapshots__/Radio.spec.tsx.snap +14 -14
  104. package/src/primitives/RadioGroup/__tests__/__snapshots__/RadioGroup.spec.tsx.snap +48 -48
  105. package/src/primitives/TextField/TextField.tsx +2 -1
  106. package/src/primitives/TextField/__tests__/TextField.spec.tsx +57 -8
  107. package/src/primitives/TextField/__tests__/__snapshots__/TextField.spec.tsx.snap +61 -55
  108. package/src/primitives/TextField/styles.ts +6 -3
  109. package/src/primitives/TextField/types.ts +1 -0
  110. package/src/theme/__tests__/createTheme.spec.ts +48 -0
  111. package/src/theme/createTheme.ts +44 -21
  112. package/src/theme/types.ts +17 -16
  113. package/src/version.ts +1 -1
@@ -67,7 +67,7 @@ describe('TextField', () => {
67
67
  expect(queryByText(message)).toBeNull();
68
68
  });
69
69
 
70
- it(`doesn't render the errorMessage if errorMessage prop is undefined`, () => {
70
+ it(`shows error style, but doesn't render the errorMessage if errorMessage prop is undefined`, () => {
71
71
  const message = 'Error message';
72
72
  const { toJSON, queryByText } = render(
73
73
  <TextField {...defaultProps} error />
@@ -111,18 +111,13 @@ describe('TextField', () => {
111
111
  });
112
112
 
113
113
  it('applies theme and style props', () => {
114
- const errorMessageText = 'Error!';
115
- const customErrorMessageStyle = { color: 'red' };
116
114
  const customFieldStyle = { color: 'orange' };
117
115
  const customLabelStyle = { color: 'blue' };
118
116
  const customStyle = { backgroundColor: 'purple' };
119
117
 
120
- const { getByTestId, getByText } = render(
118
+ const { getByTestId } = render(
121
119
  <TextField
122
120
  {...defaultProps}
123
- error
124
- errorMessage={errorMessageText}
125
- errorMessageStyle={customErrorMessageStyle}
126
121
  fieldStyle={customFieldStyle}
127
122
  labelStyle={customLabelStyle}
128
123
  style={customStyle}
@@ -135,7 +130,6 @@ describe('TextField', () => {
135
130
  const container = getByTestId(TEXTFIELD_CONTAINER_TEST_ID);
136
131
  const inputContainer = getByTestId(INPUT_CONTAINER_TEST_ID);
137
132
  const input = getByTestId(testID);
138
- const errorMessage = getByText(errorMessageText);
139
133
 
140
134
  expect(container.props.style).toStrictEqual([
141
135
  themedStyle.container,
@@ -148,6 +142,61 @@ describe('TextField', () => {
148
142
  themedStyle.field,
149
143
  customFieldStyle,
150
144
  ]);
145
+ });
146
+
147
+ it('applies theme and style props with error', () => {
148
+ const errorMessageText = 'Error!';
149
+ const customErrorMessageStyle = { color: 'red' };
150
+ const customStyle = { backgroundColor: 'purple' };
151
+
152
+ const { getByTestId, getByText } = render(
153
+ <TextField
154
+ {...defaultProps}
155
+ style={customStyle}
156
+ error
157
+ errorMessage={errorMessageText}
158
+ errorMessageStyle={customErrorMessageStyle}
159
+ />
160
+ );
161
+
162
+ const { result } = renderHook(() => useTheme());
163
+ const themedStyle = getThemedStyles(result.current);
164
+
165
+ const container = getByTestId(TEXTFIELD_CONTAINER_TEST_ID);
166
+ const inputContainer = getByTestId(INPUT_CONTAINER_TEST_ID);
167
+ const errorMessage = getByText(errorMessageText);
168
+
169
+ expect(container.props.style).toStrictEqual([
170
+ themedStyle.container,
171
+ customStyle,
172
+ ]);
173
+ expect(inputContainer.props.style).toStrictEqual({
174
+ ...themedStyle.fieldContainer,
175
+ ...themedStyle.error,
176
+ });
151
177
  expect(errorMessage.props.style).toContain(customErrorMessageStyle);
152
178
  });
179
+
180
+ it('applies theme and style props for disabled', () => {
181
+ const customStyle = { backgroundColor: 'purple' };
182
+
183
+ const { getByTestId } = render(
184
+ <TextField {...defaultProps} style={customStyle} disabled />
185
+ );
186
+
187
+ const { result } = renderHook(() => useTheme());
188
+ const themedStyle = getThemedStyles(result.current);
189
+
190
+ const container = getByTestId(TEXTFIELD_CONTAINER_TEST_ID);
191
+ const inputContainer = getByTestId(INPUT_CONTAINER_TEST_ID);
192
+
193
+ expect(container.props.style).toStrictEqual([
194
+ themedStyle.container,
195
+ customStyle,
196
+ ]);
197
+ expect(inputContainer.props.style).toStrictEqual({
198
+ ...themedStyle.fieldContainer,
199
+ ...themedStyle.disabled,
200
+ });
201
+ });
153
202
  });
@@ -6,6 +6,7 @@ exports[`TextField doesn't render the errorMessage if error prop is false 1`] =
6
6
  Array [
7
7
  Object {
8
8
  "alignItems": "flex-start",
9
+ "marginBottom": 8,
9
10
  },
10
11
  undefined,
11
12
  ]
@@ -18,9 +19,9 @@ exports[`TextField doesn't render the errorMessage if error prop is false 1`] =
18
19
  style={
19
20
  Array [
20
21
  Object {
21
- "fontSize": 14,
22
+ "fontSize": 16,
22
23
  "fontWeight": "400",
23
- "lineHeight": 21,
24
+ "lineHeight": 24,
24
25
  },
25
26
  Object {
26
27
  "color": "hsl(210, 50%, 10%)",
@@ -28,8 +29,7 @@ exports[`TextField doesn't render the errorMessage if error prop is false 1`] =
28
29
  Array [
29
30
  Object {
30
31
  "color": "hsl(210, 25%, 25%)",
31
- "lineHeight": 21,
32
- "padding": 4,
32
+ "paddingVertical": 8,
33
33
  },
34
34
  undefined,
35
35
  ],
@@ -69,6 +69,7 @@ exports[`TextField doesn't render the errorMessage if error prop is false 1`] =
69
69
  "color": "hsl(210, 50%, 10%)",
70
70
  "flexGrow": 1,
71
71
  "fontSize": 16,
72
+ "paddingHorizontal": 4,
72
73
  "paddingVertical": 12,
73
74
  },
74
75
  undefined,
@@ -80,12 +81,13 @@ exports[`TextField doesn't render the errorMessage if error prop is false 1`] =
80
81
  </View>
81
82
  `;
82
83
 
83
- exports[`TextField doesn't render the errorMessage if errorMessage prop is undefined 1`] = `
84
+ exports[`TextField renders as expected 1`] = `
84
85
  <View
85
86
  style={
86
87
  Array [
87
88
  Object {
88
89
  "alignItems": "flex-start",
90
+ "marginBottom": 8,
89
91
  },
90
92
  undefined,
91
93
  ]
@@ -98,9 +100,9 @@ exports[`TextField doesn't render the errorMessage if errorMessage prop is undef
98
100
  style={
99
101
  Array [
100
102
  Object {
101
- "fontSize": 14,
103
+ "fontSize": 16,
102
104
  "fontWeight": "400",
103
- "lineHeight": 21,
105
+ "lineHeight": 24,
104
106
  },
105
107
  Object {
106
108
  "color": "hsl(210, 50%, 10%)",
@@ -108,8 +110,7 @@ exports[`TextField doesn't render the errorMessage if errorMessage prop is undef
108
110
  Array [
109
111
  Object {
110
112
  "color": "hsl(210, 25%, 25%)",
111
- "lineHeight": 21,
112
- "padding": 4,
113
+ "paddingVertical": 8,
113
114
  },
114
115
  undefined,
115
116
  ],
@@ -149,6 +150,7 @@ exports[`TextField doesn't render the errorMessage if errorMessage prop is undef
149
150
  "color": "hsl(210, 50%, 10%)",
150
151
  "flexGrow": 1,
151
152
  "fontSize": 16,
153
+ "paddingHorizontal": 4,
152
154
  "paddingVertical": 12,
153
155
  },
154
156
  undefined,
@@ -160,12 +162,13 @@ exports[`TextField doesn't render the errorMessage if errorMessage prop is undef
160
162
  </View>
161
163
  `;
162
164
 
163
- exports[`TextField renders as expected 1`] = `
165
+ exports[`TextField renders as expected as password field 1`] = `
164
166
  <View
165
167
  style={
166
168
  Array [
167
169
  Object {
168
170
  "alignItems": "flex-start",
171
+ "marginBottom": 8,
169
172
  },
170
173
  undefined,
171
174
  ]
@@ -178,9 +181,9 @@ exports[`TextField renders as expected 1`] = `
178
181
  style={
179
182
  Array [
180
183
  Object {
181
- "fontSize": 14,
184
+ "fontSize": 16,
182
185
  "fontWeight": "400",
183
- "lineHeight": 21,
186
+ "lineHeight": 24,
184
187
  },
185
188
  Object {
186
189
  "color": "hsl(210, 50%, 10%)",
@@ -188,8 +191,7 @@ exports[`TextField renders as expected 1`] = `
188
191
  Array [
189
192
  Object {
190
193
  "color": "hsl(210, 25%, 25%)",
191
- "lineHeight": 21,
192
- "padding": 4,
194
+ "paddingVertical": 8,
193
195
  },
194
196
  undefined,
195
197
  ],
@@ -223,12 +225,14 @@ exports[`TextField renders as expected 1`] = `
223
225
  editable={true}
224
226
  placeholder="Placeholder"
225
227
  placeholderTextColor="hsl(210, 10%, 40%)"
228
+ secureTextEntry={true}
226
229
  style={
227
230
  Array [
228
231
  Object {
229
232
  "color": "hsl(210, 50%, 10%)",
230
233
  "flexGrow": 1,
231
234
  "fontSize": 16,
235
+ "paddingHorizontal": 4,
232
236
  "paddingVertical": 12,
233
237
  },
234
238
  undefined,
@@ -240,12 +244,13 @@ exports[`TextField renders as expected 1`] = `
240
244
  </View>
241
245
  `;
242
246
 
243
- exports[`TextField renders as expected as password field 1`] = `
247
+ exports[`TextField renders as expected when disabled 1`] = `
244
248
  <View
245
249
  style={
246
250
  Array [
247
251
  Object {
248
252
  "alignItems": "flex-start",
253
+ "marginBottom": 8,
249
254
  },
250
255
  undefined,
251
256
  ]
@@ -258,9 +263,9 @@ exports[`TextField renders as expected as password field 1`] = `
258
263
  style={
259
264
  Array [
260
265
  Object {
261
- "fontSize": 14,
266
+ "fontSize": 16,
262
267
  "fontWeight": "400",
263
- "lineHeight": 21,
268
+ "lineHeight": 24,
264
269
  },
265
270
  Object {
266
271
  "color": "hsl(210, 50%, 10%)",
@@ -268,8 +273,7 @@ exports[`TextField renders as expected as password field 1`] = `
268
273
  Array [
269
274
  Object {
270
275
  "color": "hsl(210, 25%, 25%)",
271
- "lineHeight": 21,
272
- "padding": 4,
276
+ "paddingVertical": 8,
273
277
  },
274
278
  undefined,
275
279
  ],
@@ -287,6 +291,7 @@ exports[`TextField renders as expected as password field 1`] = `
287
291
  "borderRadius": 4,
288
292
  "borderWidth": 1,
289
293
  "flexDirection": "row",
294
+ "opacity": 0.6,
290
295
  "paddingHorizontal": 8,
291
296
  }
292
297
  }
@@ -295,21 +300,21 @@ exports[`TextField renders as expected as password field 1`] = `
295
300
  <TextInput
296
301
  accessibilityState={
297
302
  Object {
298
- "disabled": undefined,
303
+ "disabled": true,
299
304
  }
300
305
  }
301
306
  accessible={true}
302
307
  autoCapitalize="none"
303
- editable={true}
308
+ editable={false}
304
309
  placeholder="Placeholder"
305
310
  placeholderTextColor="hsl(210, 10%, 40%)"
306
- secureTextEntry={true}
307
311
  style={
308
312
  Array [
309
313
  Object {
310
314
  "color": "hsl(210, 50%, 10%)",
311
315
  "flexGrow": 1,
312
316
  "fontSize": 16,
317
+ "paddingHorizontal": 4,
313
318
  "paddingVertical": 12,
314
319
  },
315
320
  undefined,
@@ -321,12 +326,13 @@ exports[`TextField renders as expected as password field 1`] = `
321
326
  </View>
322
327
  `;
323
328
 
324
- exports[`TextField renders as expected when disabled 1`] = `
329
+ exports[`TextField renders as expected with error message 1`] = `
325
330
  <View
326
331
  style={
327
332
  Array [
328
333
  Object {
329
334
  "alignItems": "flex-start",
335
+ "marginBottom": 8,
330
336
  },
331
337
  undefined,
332
338
  ]
@@ -339,9 +345,9 @@ exports[`TextField renders as expected when disabled 1`] = `
339
345
  style={
340
346
  Array [
341
347
  Object {
342
- "fontSize": 14,
348
+ "fontSize": 16,
343
349
  "fontWeight": "400",
344
- "lineHeight": 21,
350
+ "lineHeight": 24,
345
351
  },
346
352
  Object {
347
353
  "color": "hsl(210, 50%, 10%)",
@@ -349,8 +355,7 @@ exports[`TextField renders as expected when disabled 1`] = `
349
355
  Array [
350
356
  Object {
351
357
  "color": "hsl(210, 25%, 25%)",
352
- "lineHeight": 21,
353
- "padding": 4,
358
+ "paddingVertical": 8,
354
359
  },
355
360
  undefined,
356
361
  ],
@@ -364,11 +369,10 @@ exports[`TextField renders as expected when disabled 1`] = `
364
369
  style={
365
370
  Object {
366
371
  "alignItems": "center",
367
- "borderColor": "hsl(210, 10%, 58%)",
372
+ "borderColor": "hsl(0, 95%, 30%)",
368
373
  "borderRadius": 4,
369
374
  "borderWidth": 1,
370
375
  "flexDirection": "row",
371
- "opacity": 0.6,
372
376
  "paddingHorizontal": 8,
373
377
  }
374
378
  }
@@ -377,12 +381,12 @@ exports[`TextField renders as expected when disabled 1`] = `
377
381
  <TextInput
378
382
  accessibilityState={
379
383
  Object {
380
- "disabled": true,
384
+ "disabled": undefined,
381
385
  }
382
386
  }
383
387
  accessible={true}
384
388
  autoCapitalize="none"
385
- editable={false}
389
+ editable={true}
386
390
  placeholder="Placeholder"
387
391
  placeholderTextColor="hsl(210, 10%, 40%)"
388
392
  style={
@@ -391,6 +395,7 @@ exports[`TextField renders as expected when disabled 1`] = `
391
395
  "color": "hsl(210, 50%, 10%)",
392
396
  "flexGrow": 1,
393
397
  "fontSize": 16,
398
+ "paddingHorizontal": 4,
394
399
  "paddingVertical": 12,
395
400
  },
396
401
  undefined,
@@ -399,15 +404,34 @@ exports[`TextField renders as expected when disabled 1`] = `
399
404
  testID="textInput"
400
405
  />
401
406
  </View>
407
+ <Text
408
+ accessibilityRole="text"
409
+ style={
410
+ Array [
411
+ Object {
412
+ "fontSize": 16,
413
+ "fontWeight": "400",
414
+ "lineHeight": 24,
415
+ },
416
+ Object {
417
+ "color": "hsl(210, 50%, 10%)",
418
+ },
419
+ undefined,
420
+ ]
421
+ }
422
+ >
423
+ Error message
424
+ </Text>
402
425
  </View>
403
426
  `;
404
427
 
405
- exports[`TextField renders as expected with error message 1`] = `
428
+ exports[`TextField shows error style, but doesn't render the errorMessage if errorMessage prop is undefined 1`] = `
406
429
  <View
407
430
  style={
408
431
  Array [
409
432
  Object {
410
433
  "alignItems": "flex-start",
434
+ "marginBottom": 8,
411
435
  },
412
436
  undefined,
413
437
  ]
@@ -420,9 +444,9 @@ exports[`TextField renders as expected with error message 1`] = `
420
444
  style={
421
445
  Array [
422
446
  Object {
423
- "fontSize": 14,
447
+ "fontSize": 16,
424
448
  "fontWeight": "400",
425
- "lineHeight": 21,
449
+ "lineHeight": 24,
426
450
  },
427
451
  Object {
428
452
  "color": "hsl(210, 50%, 10%)",
@@ -430,8 +454,7 @@ exports[`TextField renders as expected with error message 1`] = `
430
454
  Array [
431
455
  Object {
432
456
  "color": "hsl(210, 25%, 25%)",
433
- "lineHeight": 21,
434
- "padding": 4,
457
+ "paddingVertical": 8,
435
458
  },
436
459
  undefined,
437
460
  ],
@@ -445,7 +468,7 @@ exports[`TextField renders as expected with error message 1`] = `
445
468
  style={
446
469
  Object {
447
470
  "alignItems": "center",
448
- "borderColor": "hsl(210, 10%, 58%)",
471
+ "borderColor": "hsl(0, 95%, 30%)",
449
472
  "borderRadius": 4,
450
473
  "borderWidth": 1,
451
474
  "flexDirection": "row",
@@ -471,6 +494,7 @@ exports[`TextField renders as expected with error message 1`] = `
471
494
  "color": "hsl(210, 50%, 10%)",
472
495
  "flexGrow": 1,
473
496
  "fontSize": 16,
497
+ "paddingHorizontal": 4,
474
498
  "paddingVertical": 12,
475
499
  },
476
500
  undefined,
@@ -479,23 +503,5 @@ exports[`TextField renders as expected with error message 1`] = `
479
503
  testID="textInput"
480
504
  />
481
505
  </View>
482
- <Text
483
- accessibilityRole="text"
484
- style={
485
- Array [
486
- Object {
487
- "fontSize": 14,
488
- "fontWeight": "400",
489
- "lineHeight": 21,
490
- },
491
- Object {
492
- "color": "hsl(210, 50%, 10%)",
493
- },
494
- undefined,
495
- ]
496
- }
497
- >
498
- Error message
499
- </Text>
500
506
  </View>
501
507
  `;
@@ -1,7 +1,6 @@
1
1
  import { Platform, StyleSheet } from 'react-native';
2
2
 
3
3
  import { StrictTheme } from '../../theme';
4
- import { getLineHeight } from '../../utils';
5
4
  import { TextFieldStyles } from './types';
6
5
 
7
6
  export const getThemedStyles = (theme: StrictTheme): TextFieldStyles => {
@@ -13,12 +12,16 @@ export const getThemedStyles = (theme: StrictTheme): TextFieldStyles => {
13
12
  return StyleSheet.create({
14
13
  container: {
15
14
  alignItems: 'flex-start',
15
+ marginBottom: space.xs,
16
16
  ...components?.textField?.container,
17
17
  },
18
18
  disabled: {
19
19
  opacity: opacities[60],
20
20
  ...components?.textField?.disabled,
21
21
  },
22
+ error: {
23
+ borderColor: colors.border.error,
24
+ },
22
25
  fieldContainer: {
23
26
  alignItems: 'center',
24
27
  borderColor: colors.border.primary,
@@ -33,14 +36,14 @@ export const getThemedStyles = (theme: StrictTheme): TextFieldStyles => {
33
36
  fontSize: fontSizes.medium,
34
37
  flexGrow: 1,
35
38
  paddingVertical: space.small,
39
+ paddingHorizontal: space.xxs,
36
40
  // this is needed because of extra padding inside the input - in Android only
37
41
  ...(Platform.OS === 'android' && { padding: 0 }),
38
42
  ...components?.textField?.field,
39
43
  },
40
44
  label: {
41
45
  color: colors.font.secondary,
42
- lineHeight: getLineHeight(fontSizes.small),
43
- padding: space.xxs,
46
+ paddingVertical: space.xs,
44
47
  ...components?.textField?.label,
45
48
  },
46
49
  });
@@ -60,6 +60,7 @@ export interface TextFieldProps extends Omit<TextInputProps, 'editable'> {
60
60
  export interface TextFieldStyles {
61
61
  container?: ViewStyle;
62
62
  disabled?: ViewStyle;
63
+ error?: ViewStyle;
63
64
  field?: TextStyle;
64
65
  fieldContainer?: ViewStyle;
65
66
  label?: TextStyle;
@@ -21,6 +21,54 @@ describe('createTheme', () => {
21
21
  });
22
22
  });
23
23
 
24
+ describe('number conversions', () => {
25
+ it('should convert strings to numbers where applicable', () => {
26
+ const { tokens } = createTheme({
27
+ tokens: {
28
+ borderWidths: {
29
+ small: '4',
30
+ medium: '1rem',
31
+ large: 6,
32
+ },
33
+ opacities: {
34
+ '10': '0.2',
35
+ },
36
+ space: {
37
+ small: 4,
38
+ medium: '6',
39
+ large: '{space.small.value}',
40
+ },
41
+ fontSizes: {
42
+ small: '1rem',
43
+ },
44
+ },
45
+ });
46
+ expect(tokens.borderWidths.small).toBe(4);
47
+ expect(tokens.borderWidths.medium).toBe(16);
48
+ expect(tokens.borderWidths.large).toBe(6);
49
+ expect(tokens.opacities['10']).toBe(0.2);
50
+ expect(tokens.space.small).toBe(4);
51
+ expect(tokens.space.medium).toBe(6);
52
+ expect(tokens.space.large).toBe(4);
53
+ expect(tokens.fontSizes.small).toBe(16);
54
+ });
55
+
56
+ it('should use the spaceModifier for space tokens with rem', () => {
57
+ const { tokens } = createTheme({
58
+ spaceModifier: 1.25,
59
+ tokens: {
60
+ space: {
61
+ small: 4,
62
+ medium: '1rem',
63
+ },
64
+ },
65
+ });
66
+
67
+ expect(tokens.space.small).toEqual(4);
68
+ expect(tokens.space.medium).toEqual(20);
69
+ });
70
+ });
71
+
24
72
  describe('with mixture of value and no value', () => {
25
73
  const { tokens } = createTheme({
26
74
  tokens: {
@@ -2,21 +2,33 @@ import deepExtend from 'style-dictionary/lib/utils/deepExtend';
2
2
  import resolveObject from 'style-dictionary/lib/utils/resolveObject';
3
3
  import usesReference from 'style-dictionary/lib/utils/references/usesReference';
4
4
  import { isFunction, setupTokens } from '@aws-amplify/ui';
5
- import { Theme, StrictTheme, ColorMode, Components } from './types';
5
+ import {
6
+ Theme,
7
+ StrictTheme,
8
+ ColorMode,
9
+ Components,
10
+ StrictTokens,
11
+ } from './types';
6
12
  import { defaultTheme } from './defaultTheme';
7
13
 
8
14
  // This will resolve all references in component themes by either
9
15
  // calling the component theme function with the already resolved base tokens
10
16
  // OR
11
17
  // resolving the component theme object
12
- const setupComponents = ({ components, tokens }: StrictTheme) => {
18
+ const setupComponents = ({
19
+ components,
20
+ tokens,
21
+ }: {
22
+ components: Components;
23
+ tokens: StrictTokens;
24
+ }) => {
13
25
  const output = components
14
26
  ? Object.entries(components).reduce(
15
27
  (acc, [key, value]) => ({
16
28
  ...acc,
17
29
  [key]: isFunction(value) ? (value(tokens) as typeof value) : value,
18
30
  }),
19
- {} as Components<'components'>
31
+ {}
20
32
  )
21
33
  : {};
22
34
 
@@ -26,6 +38,16 @@ const setupComponents = ({ components, tokens }: StrictTheme) => {
26
38
  }).components;
27
39
  };
28
40
 
41
+ const shouldParseFloatValue = (pathKey: string) =>
42
+ [
43
+ 'space',
44
+ 'borderWidths',
45
+ 'opacities',
46
+ 'fontSizes',
47
+ 'lineHeights',
48
+ 'radii',
49
+ ].includes(pathKey);
50
+
29
51
  const setupToken = ({
30
52
  token,
31
53
  path = [],
@@ -39,27 +61,28 @@ const setupToken = ({
39
61
  }): string | number => {
40
62
  const { value } = token;
41
63
  if (typeof value === 'string') {
42
- // Perform transforms
43
- if (path[0] === 'space') {
44
- if (value.includes('rem')) {
45
- return Math.floor(parseFloat(value) * 16 * spaceModifier);
46
- }
47
- }
48
- if (value.includes('rem')) {
49
- return Math.floor(parseFloat(value) * 16);
50
- }
51
- if (value.includes('px')) {
52
- return parseInt(value, 10);
53
- }
54
- if (path[0] === 'opacities') {
55
- return parseFloat(value);
56
- }
57
64
  // Remove .value from references if there is a reference
65
+ // this needs to come first so we don't get NaNs for references
58
66
  if (usesReference(value)) {
59
67
  return value.replace('.value', '');
60
68
  }
69
+
70
+ if (shouldParseFloatValue(path[0])) {
71
+ if (value.includes('rem')) {
72
+ if (path[0] === 'space') {
73
+ return Math.floor(parseFloat(value) * 16 * spaceModifier);
74
+ }
75
+ return Math.floor(parseFloat(value) * 16);
76
+ }
77
+ if (value.includes('px')) {
78
+ return parseInt(value, 10);
79
+ }
80
+ return parseFloat(value);
81
+ }
82
+
61
83
  return value;
62
84
  }
85
+
63
86
  // Font Weights in RN are strings
64
87
  if (path[0] === 'fontWeights') {
65
88
  return `${value}`;
@@ -118,14 +141,14 @@ export const createTheme = (
118
141
  }) as StrictTheme['tokens']
119
142
  );
120
143
 
121
- let { components } = mergedTheme;
144
+ let components;
122
145
 
123
146
  // Resolve component token references too
124
147
  if (mergedTheme.components) {
125
148
  components = setupComponents({
126
- ...mergedTheme,
149
+ components: mergedTheme.components,
127
150
  tokens,
128
- }) as Components<'output'>;
151
+ });
129
152
  }
130
153
 
131
154
  return { ...mergedTheme, tokens, components };