@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.
- package/CHANGELOG.md +57 -0
- package/dist/Authenticator/Authenticator.d.ts +101 -148
- package/dist/Authenticator/Authenticator.js +2 -3
- package/dist/Authenticator/Defaults/ConfirmResetPassword/ConfirmResetPassword.d.ts +13 -2
- package/dist/Authenticator/Defaults/ConfirmResetPassword/ConfirmResetPassword.js +4 -3
- package/dist/Authenticator/Defaults/ConfirmSignIn/ConfirmSignIn.d.ts +13 -2
- package/dist/Authenticator/Defaults/ConfirmSignIn/ConfirmSignIn.js +4 -3
- package/dist/Authenticator/Defaults/ConfirmSignUp/ConfirmSignUp.d.ts +13 -2
- package/dist/Authenticator/Defaults/ConfirmSignUp/ConfirmSignUp.js +4 -3
- package/dist/Authenticator/Defaults/ConfirmVerifyUser/ConfirmVerifyUser.d.ts +13 -2
- package/dist/Authenticator/Defaults/ConfirmVerifyUser/ConfirmVerifyUser.js +4 -3
- package/dist/Authenticator/Defaults/ForceNewPassword/ForceNewPassword.d.ts +13 -2
- package/dist/Authenticator/Defaults/ForceNewPassword/ForceNewPassword.js +4 -3
- package/dist/Authenticator/Defaults/ResetPassword/ResetPassword.d.ts +13 -2
- package/dist/Authenticator/Defaults/ResetPassword/ResetPassword.js +4 -3
- package/dist/Authenticator/Defaults/SetupTOTP/SetupTOTP.d.ts +13 -2
- package/dist/Authenticator/Defaults/SetupTOTP/SetupTOTP.js +4 -3
- package/dist/Authenticator/Defaults/SignIn/SignIn.d.ts +13 -2
- package/dist/Authenticator/Defaults/SignIn/SignIn.js +4 -3
- package/dist/Authenticator/Defaults/SignUp/SignUp.d.ts +13 -2
- package/dist/Authenticator/Defaults/SignUp/SignUp.js +4 -3
- package/dist/Authenticator/Defaults/VerifyUser/VerifyUser.d.ts +13 -2
- package/dist/Authenticator/Defaults/VerifyUser/VerifyUser.js +4 -3
- package/dist/Authenticator/Defaults/types.d.ts +21 -20
- package/dist/Authenticator/common/DefaultContent/styles.js +1 -2
- package/dist/Authenticator/common/DefaultContent/types.d.ts +1 -1
- package/dist/Authenticator/common/DefaultFormFields/DefaultRadioFormFields.d.ts +7 -3
- package/dist/Authenticator/common/DefaultFormFields/DefaultRadioFormFields.js +4 -3
- package/dist/Authenticator/common/DefaultFormFields/DefaultTextFormFields.d.ts +6 -2
- package/dist/Authenticator/common/DefaultFormFields/DefaultTextFormFields.js +3 -3
- package/dist/Authenticator/common/DefaultFormFields/types.d.ts +12 -3
- package/dist/Authenticator/hooks/types.d.ts +3 -2
- package/dist/Authenticator/hooks/useFieldValues/types.d.ts +4 -1
- package/dist/Authenticator/hooks/useFieldValues/useFieldValues.d.ts +1 -1
- package/dist/Authenticator/hooks/useFieldValues/useFieldValues.js +21 -3
- package/dist/Authenticator/hooks/useFieldValues/utils.d.ts +10 -1
- package/dist/Authenticator/hooks/useFieldValues/utils.js +32 -2
- package/dist/primitives/Heading/styles.js +5 -5
- package/dist/primitives/Label/styles.js +2 -2
- package/dist/primitives/TextField/TextField.js +2 -1
- package/dist/primitives/TextField/styles.js +6 -3
- package/dist/primitives/TextField/types.d.ts +1 -0
- package/dist/theme/createTheme.js +24 -18
- package/dist/theme/types.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +5 -5
- package/src/Authenticator/Authenticator.tsx +2 -6
- package/src/Authenticator/Defaults/ConfirmResetPassword/ConfirmResetPassword.tsx +7 -3
- package/src/Authenticator/Defaults/ConfirmResetPassword/__tests__/__snapshots__/ConfirmResetPassword.spec.tsx.snap +34 -30
- package/src/Authenticator/Defaults/ConfirmSignIn/ConfirmSignIn.tsx +7 -3
- package/src/Authenticator/Defaults/ConfirmSignIn/__tests__/ConfirmSignIn.spec.tsx +1 -0
- package/src/Authenticator/Defaults/ConfirmSignIn/__tests__/__snapshots__/ConfirmSignIn.spec.tsx.snap +14 -14
- package/src/Authenticator/Defaults/ConfirmSignUp/ConfirmSignUp.tsx +7 -3
- package/src/Authenticator/Defaults/ConfirmSignUp/__tests__/ConfirmSignUp.spec.tsx +1 -0
- package/src/Authenticator/Defaults/ConfirmSignUp/__tests__/__snapshots__/ConfirmSignUp.spec.tsx.snap +12 -15
- package/src/Authenticator/Defaults/ConfirmVerifyUser/ConfirmVerifyUser.tsx +7 -3
- package/src/Authenticator/Defaults/ConfirmVerifyUser/__tests__/ConfirmVerifyUser.spec.tsx +1 -0
- package/src/Authenticator/Defaults/ConfirmVerifyUser/__tests__/__snapshots__/ConfirmVerifyUser.spec.tsx.snap +6 -9
- package/src/Authenticator/Defaults/ForceNewPassword/ForceNewPassword.tsx +7 -3
- package/src/Authenticator/Defaults/ForceNewPassword/__tests__/__snapshots__/ForceNewPassword.spec.tsx.snap +13 -10
- package/src/Authenticator/Defaults/ResetPassword/ResetPassword.tsx +7 -3
- package/src/Authenticator/Defaults/ResetPassword/__tests__/ResetPassword.spec.tsx +1 -0
- package/src/Authenticator/Defaults/ResetPassword/__tests__/__snapshots__/ResetPassword.spec.tsx.snap +14 -14
- package/src/Authenticator/Defaults/SetupTOTP/SetupTOTP.tsx +7 -3
- package/src/Authenticator/Defaults/SetupTOTP/__tests__/SetupTOTP.spec.tsx +1 -0
- package/src/Authenticator/Defaults/SetupTOTP/__tests__/__snapshots__/SetupTOTP.spec.tsx.snap +22 -22
- package/src/Authenticator/Defaults/SignIn/SignIn.tsx +7 -3
- package/src/Authenticator/Defaults/SignIn/__tests__/SignIn.spec.tsx +1 -0
- package/src/Authenticator/Defaults/SignIn/__tests__/__snapshots__/SignIn.spec.tsx.snap +36 -33
- package/src/Authenticator/Defaults/SignUp/SignUp.tsx +7 -3
- package/src/Authenticator/Defaults/SignUp/__tests__/__snapshots__/SignUp.spec.tsx.snap +111 -96
- package/src/Authenticator/Defaults/VerifyUser/VerifyUser.tsx +7 -3
- package/src/Authenticator/Defaults/VerifyUser/__tests__/VerifyUser.spec.tsx +1 -0
- package/src/Authenticator/Defaults/VerifyUser/__tests__/__snapshots__/VerifyUser.spec.tsx.snap +16 -18
- package/src/Authenticator/Defaults/types.ts +63 -49
- package/src/Authenticator/__tests__/Authenticator.spec.tsx +16 -19
- package/src/Authenticator/__tests__/__snapshots__/Authenticator.spec.tsx.snap +1 -9
- package/src/Authenticator/__tests__/withAuthenticator.spec.tsx +1 -1
- package/src/Authenticator/common/DefaultContent/styles.ts +1 -2
- package/src/Authenticator/common/DefaultContent/types.ts +1 -4
- package/src/Authenticator/common/DefaultFormFields/DefaultRadioFormFields.tsx +8 -6
- package/src/Authenticator/common/DefaultFormFields/DefaultTextFormFields.tsx +10 -7
- package/src/Authenticator/common/DefaultFormFields/types.ts +15 -5
- package/src/Authenticator/common/DefaultHeader/__tests__/__snapshots__/DefaultHeader.spec.tsx.snap +1 -1
- package/src/Authenticator/common/FederatedProviderButton/__tests__/__snapshots__/FederatedProviderButton.spec.tsx.snap +4 -4
- package/src/Authenticator/common/FederatedProviderButtons/__tests__/__snapshots__/FederatedProviderButtons.spec.tsx.snap +4 -4
- package/src/Authenticator/hooks/types.ts +3 -0
- package/src/Authenticator/hooks/useFieldValues/__tests__/useFieldValues.spec.ts +75 -2
- package/src/Authenticator/hooks/useFieldValues/__tests__/utils.spec.ts +67 -1
- package/src/Authenticator/hooks/useFieldValues/types.ts +5 -0
- package/src/Authenticator/hooks/useFieldValues/useFieldValues.ts +26 -1
- package/src/Authenticator/hooks/useFieldValues/utils.ts +44 -1
- package/src/primitives/Checkbox/__tests__/__snapshots__/Checkbox.spec.tsx.snap +14 -14
- package/src/primitives/Divider/__tests__/__snapshots__/Divider.spec.tsx.snap +4 -4
- package/src/primitives/Heading/__tests__/__snapshots__/Heading.spec.tsx.snap +7 -7
- package/src/primitives/Heading/styles.ts +5 -5
- package/src/primitives/Label/__tests__/__snapshots__/Label.spec.tsx.snap +8 -8
- package/src/primitives/Label/styles.ts +2 -2
- package/src/primitives/PasswordField/__tests__/__snapshots__/PasswordField.spec.tsx.snap +25 -20
- package/src/primitives/PhoneNumberField/__tests__/__snapshots__/PhoneNumberField.spec.tsx.snap +6 -0
- package/src/primitives/Radio/__tests__/__snapshots__/Radio.spec.tsx.snap +14 -14
- package/src/primitives/RadioGroup/__tests__/__snapshots__/RadioGroup.spec.tsx.snap +48 -48
- package/src/primitives/TextField/TextField.tsx +2 -1
- package/src/primitives/TextField/__tests__/TextField.spec.tsx +57 -8
- package/src/primitives/TextField/__tests__/__snapshots__/TextField.spec.tsx.snap +61 -55
- package/src/primitives/TextField/styles.ts +6 -3
- package/src/primitives/TextField/types.ts +1 -0
- package/src/theme/__tests__/createTheme.spec.ts +48 -0
- package/src/theme/createTheme.ts +44 -21
- package/src/theme/types.ts +17 -16
- package/src/version.ts +1 -1
|
@@ -2,7 +2,10 @@ import { act, renderHook } from '@testing-library/react-hooks';
|
|
|
2
2
|
import { NativeSyntheticEvent, TextInputFocusEventData } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { Logger } from 'aws-amplify';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
UnverifiedContactMethodType,
|
|
7
|
+
authenticatorTextUtil,
|
|
8
|
+
} from '@aws-amplify/ui';
|
|
6
9
|
import {
|
|
7
10
|
RadioFieldOptions,
|
|
8
11
|
TextFieldOptionsType,
|
|
@@ -58,6 +61,7 @@ describe('useFieldValues', () => {
|
|
|
58
61
|
value: undefined,
|
|
59
62
|
},
|
|
60
63
|
],
|
|
64
|
+
fieldValidationErrors: {},
|
|
61
65
|
handleFormSubmit: expect.any(Function),
|
|
62
66
|
});
|
|
63
67
|
});
|
|
@@ -80,6 +84,7 @@ describe('useFieldValues', () => {
|
|
|
80
84
|
value: undefined,
|
|
81
85
|
},
|
|
82
86
|
],
|
|
87
|
+
fieldValidationErrors: {},
|
|
83
88
|
handleFormSubmit: expect.any(Function),
|
|
84
89
|
});
|
|
85
90
|
});
|
|
@@ -95,6 +100,7 @@ describe('useFieldValues', () => {
|
|
|
95
100
|
expect(result.current).toStrictEqual({
|
|
96
101
|
disableFormSubmit: true,
|
|
97
102
|
fields: [{ ...radioField, onChange: expect.any(Function) }],
|
|
103
|
+
fieldValidationErrors: {},
|
|
98
104
|
handleFormSubmit: expect.any(Function),
|
|
99
105
|
});
|
|
100
106
|
});
|
|
@@ -105,6 +111,7 @@ describe('useFieldValues', () => {
|
|
|
105
111
|
expect(result.current).toStrictEqual({
|
|
106
112
|
disableFormSubmit: false,
|
|
107
113
|
fields: mockfields,
|
|
114
|
+
fieldValidationErrors: {},
|
|
108
115
|
handleFormSubmit: expect.any(Function),
|
|
109
116
|
});
|
|
110
117
|
});
|
|
@@ -129,7 +136,9 @@ describe('useFieldValues', () => {
|
|
|
129
136
|
const mockEvent = {
|
|
130
137
|
nativeEvent: { target: 1 },
|
|
131
138
|
} as NativeSyntheticEvent<TextInputFocusEventData>;
|
|
132
|
-
|
|
139
|
+
act(() => {
|
|
140
|
+
result.current.fields[0].onBlur?.(mockEvent);
|
|
141
|
+
});
|
|
133
142
|
expect(props.handleBlur).toHaveBeenCalledTimes(1);
|
|
134
143
|
expect(props.handleBlur).toHaveBeenCalledWith({
|
|
135
144
|
name: textField.name,
|
|
@@ -153,6 +162,68 @@ describe('useFieldValues', () => {
|
|
|
153
162
|
});
|
|
154
163
|
});
|
|
155
164
|
|
|
165
|
+
it('runs validations for email fields', () => {
|
|
166
|
+
const emailField = {
|
|
167
|
+
label: 'test',
|
|
168
|
+
type: 'email',
|
|
169
|
+
name: 'invalid_email',
|
|
170
|
+
value: 'test@',
|
|
171
|
+
} as TextFieldOptionsType;
|
|
172
|
+
const phoneTextField = {
|
|
173
|
+
type: 'phone',
|
|
174
|
+
name: 'testPhone',
|
|
175
|
+
} as TextFieldOptionsType;
|
|
176
|
+
const { result } = renderHook(() =>
|
|
177
|
+
useFieldValues({
|
|
178
|
+
...props,
|
|
179
|
+
fields: [emailField, phoneTextField],
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
const mockEvent = {
|
|
183
|
+
nativeEvent: { target: 1 },
|
|
184
|
+
} as NativeSyntheticEvent<TextInputFocusEventData>;
|
|
185
|
+
act(() => {
|
|
186
|
+
result.current.fields[0].onBlur?.(mockEvent);
|
|
187
|
+
});
|
|
188
|
+
expect(props.handleBlur).toHaveBeenCalledTimes(1);
|
|
189
|
+
expect(props.handleBlur).toHaveBeenCalledWith({
|
|
190
|
+
name: emailField.name,
|
|
191
|
+
value: undefined,
|
|
192
|
+
});
|
|
193
|
+
expect(result.current.fieldValidationErrors).toStrictEqual({
|
|
194
|
+
[emailField.name]: [authenticatorTextUtil.getInvalidEmailText()],
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('runs validations for required fields', () => {
|
|
199
|
+
const requiredField = {
|
|
200
|
+
label: 'test',
|
|
201
|
+
type: 'password',
|
|
202
|
+
name: 'required',
|
|
203
|
+
required: true,
|
|
204
|
+
} as TextFieldOptionsType;
|
|
205
|
+
const { result } = renderHook(() =>
|
|
206
|
+
useFieldValues({
|
|
207
|
+
...props,
|
|
208
|
+
fields: [requiredField],
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
const mockEvent = {
|
|
212
|
+
nativeEvent: { target: 1 },
|
|
213
|
+
} as NativeSyntheticEvent<TextInputFocusEventData>;
|
|
214
|
+
act(() => {
|
|
215
|
+
result.current.fields[0].onBlur?.(mockEvent);
|
|
216
|
+
});
|
|
217
|
+
expect(props.handleBlur).toHaveBeenCalledTimes(1);
|
|
218
|
+
expect(props.handleBlur).toHaveBeenCalledWith({
|
|
219
|
+
name: requiredField.name,
|
|
220
|
+
value: undefined,
|
|
221
|
+
});
|
|
222
|
+
expect(result.current.fieldValidationErrors).toStrictEqual({
|
|
223
|
+
[requiredField.name]: [authenticatorTextUtil.getRequiredFieldText()],
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
156
227
|
it('calls expected handlers for radios', () => {
|
|
157
228
|
const { result } = renderHook(() =>
|
|
158
229
|
useFieldValues({
|
|
@@ -234,6 +305,7 @@ describe('useFieldValues', () => {
|
|
|
234
305
|
value: undefined,
|
|
235
306
|
},
|
|
236
307
|
],
|
|
308
|
+
fieldValidationErrors: {},
|
|
237
309
|
handleFormSubmit: expect.any(Function),
|
|
238
310
|
});
|
|
239
311
|
});
|
|
@@ -267,6 +339,7 @@ describe('useFieldValues', () => {
|
|
|
267
339
|
value: mockValue,
|
|
268
340
|
},
|
|
269
341
|
],
|
|
342
|
+
fieldValidationErrors: {},
|
|
270
343
|
handleFormSubmit: expect.any(Function),
|
|
271
344
|
});
|
|
272
345
|
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Logger } from 'aws-amplify';
|
|
2
|
-
import {
|
|
2
|
+
import { authenticatorTextUtil } from '@aws-amplify/ui';
|
|
3
3
|
|
|
4
|
+
import { TextFieldOptionsType, TypedField } from '../../types';
|
|
4
5
|
import {
|
|
5
6
|
getRouteTypedFields,
|
|
6
7
|
getSanitizedRadioFields,
|
|
7
8
|
getSanitizedTextFields,
|
|
9
|
+
runFieldValidation,
|
|
8
10
|
} from '../utils';
|
|
9
11
|
|
|
10
12
|
const warnSpy = jest.spyOn(Logger.prototype, 'warn');
|
|
@@ -214,3 +216,67 @@ describe('getRouteTypedFields', () => {
|
|
|
214
216
|
expect(fields).toStrictEqual(expected);
|
|
215
217
|
});
|
|
216
218
|
});
|
|
219
|
+
|
|
220
|
+
describe('runFieldValidation', () => {
|
|
221
|
+
const { getInvalidEmailText, getRequiredFieldText } = authenticatorTextUtil;
|
|
222
|
+
const field: TextFieldOptionsType = {
|
|
223
|
+
required: true,
|
|
224
|
+
type: 'email',
|
|
225
|
+
name: 'email',
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
it('should return an empty array when no errors are found', () => {
|
|
229
|
+
const value = 'test@example.com';
|
|
230
|
+
const stateValidations = {};
|
|
231
|
+
|
|
232
|
+
const result = runFieldValidation(field, value, stateValidations);
|
|
233
|
+
|
|
234
|
+
expect(result).toEqual([]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should return an array with the required field error when value is missing', () => {
|
|
238
|
+
const value = undefined;
|
|
239
|
+
const stateValidations = {};
|
|
240
|
+
|
|
241
|
+
const result = runFieldValidation(field, value, stateValidations);
|
|
242
|
+
|
|
243
|
+
expect(result).toEqual([getRequiredFieldText(), getInvalidEmailText()]);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should return an array with the invalid email error when email value is invalid', () => {
|
|
247
|
+
const value = 'invalid-email';
|
|
248
|
+
const stateValidations = {};
|
|
249
|
+
|
|
250
|
+
const result = runFieldValidation(field, value, stateValidations);
|
|
251
|
+
|
|
252
|
+
expect(result).toEqual([getInvalidEmailText()]);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should include state machine validation errors in the result', () => {
|
|
256
|
+
const value = 'test@example.com';
|
|
257
|
+
const errorMessage = 'Email already exists.';
|
|
258
|
+
const stateValidations = {
|
|
259
|
+
email: errorMessage,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const result = runFieldValidation(field, value, stateValidations);
|
|
263
|
+
|
|
264
|
+
expect(result).toEqual([errorMessage]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should concatenate state machine validation errors with other errors', () => {
|
|
268
|
+
const value = undefined;
|
|
269
|
+
const errorMessage = 'Email already exists.';
|
|
270
|
+
const stateValidations = {
|
|
271
|
+
email: errorMessage,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const result = runFieldValidation(field, value, stateValidations);
|
|
275
|
+
|
|
276
|
+
expect(result).toEqual([
|
|
277
|
+
getRequiredFieldText(),
|
|
278
|
+
getInvalidEmailText(),
|
|
279
|
+
errorMessage,
|
|
280
|
+
]);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AuthenticatorComponentDefaultProps,
|
|
3
3
|
AuthenticatorRouteComponentName,
|
|
4
|
+
AuthenticatorMachineContext,
|
|
4
5
|
} from '@aws-amplify/ui-react-core';
|
|
6
|
+
import { ValidationError } from '@aws-amplify/ui';
|
|
5
7
|
|
|
6
8
|
import { TypedField } from '../types';
|
|
7
9
|
|
|
@@ -26,10 +28,13 @@ export interface UseFieldValuesParams<FieldType extends TypedField> {
|
|
|
26
28
|
* machine "SUBMIT"" event handler, validates `field` value against machine validation rules
|
|
27
29
|
*/
|
|
28
30
|
handleSubmit: MachineEventHandlers['handleSubmit'];
|
|
31
|
+
|
|
32
|
+
validationErrors?: AuthenticatorMachineContext['validationErrors'];
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export interface UseFieldValues<FieldType extends TypedField> {
|
|
32
36
|
fields: FieldType[]; // return either radio or text
|
|
37
|
+
fieldValidationErrors: ValidationError | undefined;
|
|
33
38
|
disableFormSubmit: boolean;
|
|
34
39
|
handleFormSubmit: () => void;
|
|
35
40
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useMemo, useState } from 'react';
|
|
2
2
|
import { Logger } from 'aws-amplify';
|
|
3
|
+
import { ValidationError } from '@aws-amplify/ui';
|
|
3
4
|
|
|
4
5
|
import { OnChangeText, TextFieldOnBlur, TypedField } from '../types';
|
|
5
6
|
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
getSanitizedTextFields,
|
|
9
10
|
getSanitizedRadioFields,
|
|
10
11
|
isRadioFieldOptions,
|
|
12
|
+
runFieldValidation,
|
|
11
13
|
} from './utils';
|
|
12
14
|
|
|
13
15
|
const logger = new Logger('Authenticator');
|
|
@@ -18,8 +20,12 @@ export default function useFieldValues<FieldType extends TypedField>({
|
|
|
18
20
|
handleBlur,
|
|
19
21
|
handleChange,
|
|
20
22
|
handleSubmit,
|
|
23
|
+
validationErrors,
|
|
21
24
|
}: UseFieldValuesParams<FieldType>): UseFieldValues<FieldType> {
|
|
22
25
|
const [values, setValues] = useState<Record<string, string>>({});
|
|
26
|
+
const [touched, setTouched] = useState<Record<string, boolean>>({});
|
|
27
|
+
const [fieldValidationErrors, setFieldValidationErrors] =
|
|
28
|
+
useState<ValidationError>({});
|
|
23
29
|
const isRadioFieldComponent = componentName === 'VerifyUser';
|
|
24
30
|
|
|
25
31
|
const sanitizedFields = useMemo(() => {
|
|
@@ -53,11 +59,18 @@ export default function useFieldValues<FieldType extends TypedField>({
|
|
|
53
59
|
const { name, label, labelHidden, ...rest } = field;
|
|
54
60
|
|
|
55
61
|
const onBlur: TextFieldOnBlur = (event) => {
|
|
62
|
+
setTouched({ ...touched, [name]: true });
|
|
63
|
+
|
|
56
64
|
// call `onBlur` passed as text `field` option
|
|
57
65
|
field.onBlur?.(event);
|
|
58
66
|
|
|
59
67
|
// call machine blur handler
|
|
60
68
|
handleBlur({ name, value: values[name] });
|
|
69
|
+
|
|
70
|
+
setFieldValidationErrors({
|
|
71
|
+
...fieldValidationErrors,
|
|
72
|
+
[name]: runFieldValidation(field, values[name], validationErrors),
|
|
73
|
+
});
|
|
61
74
|
};
|
|
62
75
|
|
|
63
76
|
const onChangeText: OnChangeText = (value) => {
|
|
@@ -67,6 +80,13 @@ export default function useFieldValues<FieldType extends TypedField>({
|
|
|
67
80
|
// call machine change handler
|
|
68
81
|
handleChange({ name, value });
|
|
69
82
|
|
|
83
|
+
if (touched[name]) {
|
|
84
|
+
setFieldValidationErrors({
|
|
85
|
+
...fieldValidationErrors,
|
|
86
|
+
[name]: runFieldValidation(field, value, validationErrors),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
70
90
|
setValues({ ...values, [name]: value });
|
|
71
91
|
};
|
|
72
92
|
|
|
@@ -112,5 +132,10 @@ export default function useFieldValues<FieldType extends TypedField>({
|
|
|
112
132
|
handleSubmit?.(submitValue);
|
|
113
133
|
};
|
|
114
134
|
|
|
115
|
-
return {
|
|
135
|
+
return {
|
|
136
|
+
fields: fieldsWithHandlers,
|
|
137
|
+
disableFormSubmit,
|
|
138
|
+
fieldValidationErrors: { ...fieldValidationErrors, ...validationErrors },
|
|
139
|
+
handleFormSubmit,
|
|
140
|
+
};
|
|
116
141
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Logger } from 'aws-amplify';
|
|
2
2
|
import {
|
|
3
|
+
authenticatorTextUtil,
|
|
4
|
+
isString,
|
|
3
5
|
isUnverifiedContactMethodType,
|
|
6
|
+
isValidEmail,
|
|
4
7
|
UnverifiedContactMethodType,
|
|
8
|
+
ValidationError,
|
|
5
9
|
} from '@aws-amplify/ui';
|
|
6
10
|
import {
|
|
7
11
|
AuthenticatorLegacyField,
|
|
@@ -14,12 +18,15 @@ import {
|
|
|
14
18
|
AuthenticatorFieldTypeKey,
|
|
15
19
|
MachineFieldTypeKey,
|
|
16
20
|
RadioFieldOptions,
|
|
21
|
+
TextFieldOptionsType,
|
|
17
22
|
TypedField,
|
|
18
23
|
} from '../types';
|
|
19
24
|
import { KEY_ALLOW_LIST } from './constants';
|
|
20
25
|
|
|
21
26
|
const logger = new Logger('Authenticator');
|
|
22
27
|
|
|
28
|
+
const { getInvalidEmailText, getRequiredFieldText } = authenticatorTextUtil;
|
|
29
|
+
|
|
23
30
|
export const isRadioFieldOptions = (
|
|
24
31
|
field: TypedField
|
|
25
32
|
): field is RadioFieldOptions => field?.type === 'radio';
|
|
@@ -109,7 +116,8 @@ const isKeyAllowed = (key: string) =>
|
|
|
109
116
|
|
|
110
117
|
const isValidMachineFieldType = (
|
|
111
118
|
type: string | undefined
|
|
112
|
-
): type is MachineFieldTypeKey =>
|
|
119
|
+
): type is MachineFieldTypeKey =>
|
|
120
|
+
type === 'password' || type === 'tel' || type == 'email';
|
|
113
121
|
|
|
114
122
|
const getFieldType = (type: string | undefined): AuthenticatorFieldTypeKey => {
|
|
115
123
|
if (isValidMachineFieldType(type)) {
|
|
@@ -176,3 +184,38 @@ export function getRouteTypedFields({
|
|
|
176
184
|
|
|
177
185
|
return isVerifyUserRoute ? radioFields : getTypedFields(fields);
|
|
178
186
|
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
*
|
|
190
|
+
* @param {TextFieldOptionsType} field text field type
|
|
191
|
+
* @param {string | undefined} value text field value
|
|
192
|
+
* @param {string[]} stateValidations validation errors array from state machine
|
|
193
|
+
* @returns {string[]} field errors array
|
|
194
|
+
*/
|
|
195
|
+
export const runFieldValidation = (
|
|
196
|
+
field: TextFieldOptionsType,
|
|
197
|
+
value: string | undefined,
|
|
198
|
+
stateValidations: ValidationError | undefined
|
|
199
|
+
): string[] => {
|
|
200
|
+
const fieldErrors: string[] = [];
|
|
201
|
+
if (field.required && !value) {
|
|
202
|
+
fieldErrors.push(getRequiredFieldText());
|
|
203
|
+
}
|
|
204
|
+
if (field.type === 'email') {
|
|
205
|
+
if (!isValidEmail(value)) {
|
|
206
|
+
fieldErrors.push(getInvalidEmailText());
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// add state machine validation errors, if any
|
|
211
|
+
const stateFieldValidation = stateValidations?.[field.name];
|
|
212
|
+
if (stateFieldValidation) {
|
|
213
|
+
if (isString(stateFieldValidation)) {
|
|
214
|
+
fieldErrors.push(stateFieldValidation);
|
|
215
|
+
} else {
|
|
216
|
+
return fieldErrors.concat(stateFieldValidation);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return fieldErrors;
|
|
221
|
+
};
|
|
@@ -51,9 +51,9 @@ exports[`Checkbox applies label position prop 1`] = `
|
|
|
51
51
|
style={
|
|
52
52
|
Array [
|
|
53
53
|
Object {
|
|
54
|
-
"fontSize":
|
|
54
|
+
"fontSize": 16,
|
|
55
55
|
"fontWeight": "400",
|
|
56
|
-
"lineHeight":
|
|
56
|
+
"lineHeight": 24,
|
|
57
57
|
},
|
|
58
58
|
Object {
|
|
59
59
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -120,9 +120,9 @@ exports[`Checkbox applies theme and style props 1`] = `
|
|
|
120
120
|
style={
|
|
121
121
|
Array [
|
|
122
122
|
Object {
|
|
123
|
-
"fontSize":
|
|
123
|
+
"fontSize": 16,
|
|
124
124
|
"fontWeight": "400",
|
|
125
|
-
"lineHeight":
|
|
125
|
+
"lineHeight": 24,
|
|
126
126
|
},
|
|
127
127
|
Object {
|
|
128
128
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -193,9 +193,9 @@ exports[`Checkbox renders as expected when disabled 1`] = `
|
|
|
193
193
|
style={
|
|
194
194
|
Array [
|
|
195
195
|
Object {
|
|
196
|
-
"fontSize":
|
|
196
|
+
"fontSize": 16,
|
|
197
197
|
"fontWeight": "400",
|
|
198
|
-
"lineHeight":
|
|
198
|
+
"lineHeight": 24,
|
|
199
199
|
},
|
|
200
200
|
Object {
|
|
201
201
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -260,9 +260,9 @@ exports[`Checkbox renders as expected when passing a number to the size prop 1`]
|
|
|
260
260
|
style={
|
|
261
261
|
Array [
|
|
262
262
|
Object {
|
|
263
|
-
"fontSize":
|
|
263
|
+
"fontSize": 16,
|
|
264
264
|
"fontWeight": "400",
|
|
265
|
-
"lineHeight":
|
|
265
|
+
"lineHeight": 24,
|
|
266
266
|
},
|
|
267
267
|
Object {
|
|
268
268
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -327,9 +327,9 @@ exports[`Checkbox renders as expected when selected is false 1`] = `
|
|
|
327
327
|
style={
|
|
328
328
|
Array [
|
|
329
329
|
Object {
|
|
330
|
-
"fontSize":
|
|
330
|
+
"fontSize": 16,
|
|
331
331
|
"fontWeight": "400",
|
|
332
|
-
"lineHeight":
|
|
332
|
+
"lineHeight": 24,
|
|
333
333
|
},
|
|
334
334
|
Object {
|
|
335
335
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -394,9 +394,9 @@ exports[`Checkbox renders as expected when selected is true 1`] = `
|
|
|
394
394
|
style={
|
|
395
395
|
Array [
|
|
396
396
|
Object {
|
|
397
|
-
"fontSize":
|
|
397
|
+
"fontSize": 16,
|
|
398
398
|
"fontWeight": "400",
|
|
399
|
-
"lineHeight":
|
|
399
|
+
"lineHeight": 24,
|
|
400
400
|
},
|
|
401
401
|
Object {
|
|
402
402
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -461,9 +461,9 @@ exports[`Checkbox renders as expected with custom accessibilityRole 1`] = `
|
|
|
461
461
|
style={
|
|
462
462
|
Array [
|
|
463
463
|
Object {
|
|
464
|
-
"fontSize":
|
|
464
|
+
"fontSize": 16,
|
|
465
465
|
"fontWeight": "400",
|
|
466
|
-
"lineHeight":
|
|
466
|
+
"lineHeight": 24,
|
|
467
467
|
},
|
|
468
468
|
Object {
|
|
469
469
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -32,9 +32,9 @@ exports[`Divider applies theme and style props 1`] = `
|
|
|
32
32
|
style={
|
|
33
33
|
Array [
|
|
34
34
|
Object {
|
|
35
|
-
"fontSize":
|
|
35
|
+
"fontSize": 16,
|
|
36
36
|
"fontWeight": "400",
|
|
37
|
-
"lineHeight":
|
|
37
|
+
"lineHeight": 24,
|
|
38
38
|
},
|
|
39
39
|
Object {
|
|
40
40
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -101,9 +101,9 @@ exports[`Divider renders as expected with label 1`] = `
|
|
|
101
101
|
style={
|
|
102
102
|
Array [
|
|
103
103
|
Object {
|
|
104
|
-
"fontSize":
|
|
104
|
+
"fontSize": 16,
|
|
105
105
|
"fontWeight": "400",
|
|
106
|
-
"lineHeight":
|
|
106
|
+
"lineHeight": 24,
|
|
107
107
|
},
|
|
108
108
|
Object {
|
|
109
109
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -10,7 +10,7 @@ exports[`Heading applies theme and style props 1`] = `
|
|
|
10
10
|
},
|
|
11
11
|
Object {
|
|
12
12
|
"fontSize": 14,
|
|
13
|
-
"fontWeight": "
|
|
13
|
+
"fontWeight": "700",
|
|
14
14
|
"lineHeight": 21,
|
|
15
15
|
},
|
|
16
16
|
Object {
|
|
@@ -33,7 +33,7 @@ exports[`Heading renders a level 1 Heading as expected 1`] = `
|
|
|
33
33
|
},
|
|
34
34
|
Object {
|
|
35
35
|
"fontSize": 40,
|
|
36
|
-
"fontWeight": "
|
|
36
|
+
"fontWeight": "300",
|
|
37
37
|
"lineHeight": 60,
|
|
38
38
|
},
|
|
39
39
|
undefined,
|
|
@@ -54,7 +54,7 @@ exports[`Heading renders a level 2 Heading as expected 1`] = `
|
|
|
54
54
|
},
|
|
55
55
|
Object {
|
|
56
56
|
"fontSize": 32,
|
|
57
|
-
"fontWeight": "
|
|
57
|
+
"fontWeight": "400",
|
|
58
58
|
"lineHeight": 48,
|
|
59
59
|
},
|
|
60
60
|
undefined,
|
|
@@ -75,7 +75,7 @@ exports[`Heading renders a level 3 Heading as expected 1`] = `
|
|
|
75
75
|
},
|
|
76
76
|
Object {
|
|
77
77
|
"fontSize": 24,
|
|
78
|
-
"fontWeight": "
|
|
78
|
+
"fontWeight": "500",
|
|
79
79
|
"lineHeight": 36,
|
|
80
80
|
},
|
|
81
81
|
undefined,
|
|
@@ -117,7 +117,7 @@ exports[`Heading renders a level 5 Heading as expected 1`] = `
|
|
|
117
117
|
},
|
|
118
118
|
Object {
|
|
119
119
|
"fontSize": 16,
|
|
120
|
-
"fontWeight": "
|
|
120
|
+
"fontWeight": "600",
|
|
121
121
|
"lineHeight": 24,
|
|
122
122
|
},
|
|
123
123
|
undefined,
|
|
@@ -138,7 +138,7 @@ exports[`Heading renders a level 6 Heading as expected 1`] = `
|
|
|
138
138
|
},
|
|
139
139
|
Object {
|
|
140
140
|
"fontSize": 14,
|
|
141
|
-
"fontWeight": "
|
|
141
|
+
"fontWeight": "700",
|
|
142
142
|
"lineHeight": 21,
|
|
143
143
|
},
|
|
144
144
|
undefined,
|
|
@@ -159,7 +159,7 @@ exports[`Heading renders a level 6 Heading by default 1`] = `
|
|
|
159
159
|
},
|
|
160
160
|
Object {
|
|
161
161
|
"fontSize": 14,
|
|
162
|
-
"fontWeight": "
|
|
162
|
+
"fontWeight": "700",
|
|
163
163
|
"lineHeight": 21,
|
|
164
164
|
},
|
|
165
165
|
undefined,
|
|
@@ -17,19 +17,19 @@ export const getThemedStyles = (theme: StrictTheme): HeadingStyles => {
|
|
|
17
17
|
},
|
|
18
18
|
1: {
|
|
19
19
|
fontSize: fontSizes.xxxl,
|
|
20
|
-
fontWeight: fontWeights.
|
|
20
|
+
fontWeight: fontWeights.light,
|
|
21
21
|
lineHeight: getLineHeight(fontSizes.xxxl),
|
|
22
22
|
...components?.heading?.[1],
|
|
23
23
|
},
|
|
24
24
|
2: {
|
|
25
25
|
fontSize: fontSizes.xxl,
|
|
26
|
-
fontWeight: fontWeights.
|
|
26
|
+
fontWeight: fontWeights.normal,
|
|
27
27
|
lineHeight: getLineHeight(fontSizes.xxl),
|
|
28
28
|
...components?.heading?.[2],
|
|
29
29
|
},
|
|
30
30
|
3: {
|
|
31
31
|
fontSize: fontSizes.xl,
|
|
32
|
-
fontWeight: fontWeights.
|
|
32
|
+
fontWeight: fontWeights.medium,
|
|
33
33
|
lineHeight: getLineHeight(fontSizes.xl),
|
|
34
34
|
...components?.heading?.[3],
|
|
35
35
|
},
|
|
@@ -41,13 +41,13 @@ export const getThemedStyles = (theme: StrictTheme): HeadingStyles => {
|
|
|
41
41
|
},
|
|
42
42
|
5: {
|
|
43
43
|
fontSize: fontSizes.medium,
|
|
44
|
-
fontWeight: fontWeights.
|
|
44
|
+
fontWeight: fontWeights.semibold,
|
|
45
45
|
lineHeight: getLineHeight(fontSizes.medium),
|
|
46
46
|
...components?.heading?.[5],
|
|
47
47
|
},
|
|
48
48
|
6: {
|
|
49
49
|
fontSize: fontSizes.small,
|
|
50
|
-
fontWeight: fontWeights.
|
|
50
|
+
fontWeight: fontWeights.bold,
|
|
51
51
|
lineHeight: getLineHeight(fontSizes.small),
|
|
52
52
|
...components?.heading?.[6],
|
|
53
53
|
},
|
|
@@ -6,9 +6,9 @@ exports[`Label applies accessibility role 1`] = `
|
|
|
6
6
|
style={
|
|
7
7
|
Array [
|
|
8
8
|
Object {
|
|
9
|
-
"fontSize":
|
|
9
|
+
"fontSize": 16,
|
|
10
10
|
"fontWeight": "400",
|
|
11
|
-
"lineHeight":
|
|
11
|
+
"lineHeight": 24,
|
|
12
12
|
},
|
|
13
13
|
Object {
|
|
14
14
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -28,9 +28,9 @@ exports[`Label renders a default Label 1`] = `
|
|
|
28
28
|
style={
|
|
29
29
|
Array [
|
|
30
30
|
Object {
|
|
31
|
-
"fontSize":
|
|
31
|
+
"fontSize": 16,
|
|
32
32
|
"fontWeight": "400",
|
|
33
|
-
"lineHeight":
|
|
33
|
+
"lineHeight": 24,
|
|
34
34
|
},
|
|
35
35
|
Object {
|
|
36
36
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -50,9 +50,9 @@ exports[`Label should apply theme and style props 1`] = `
|
|
|
50
50
|
style={
|
|
51
51
|
Array [
|
|
52
52
|
Object {
|
|
53
|
-
"fontSize":
|
|
53
|
+
"fontSize": 16,
|
|
54
54
|
"fontWeight": "400",
|
|
55
|
-
"lineHeight":
|
|
55
|
+
"lineHeight": 24,
|
|
56
56
|
},
|
|
57
57
|
Object {
|
|
58
58
|
"color": "hsl(210, 50%, 10%)",
|
|
@@ -74,9 +74,9 @@ exports[`Label should apply variant styles 1`] = `
|
|
|
74
74
|
style={
|
|
75
75
|
Array [
|
|
76
76
|
Object {
|
|
77
|
-
"fontSize":
|
|
77
|
+
"fontSize": 16,
|
|
78
78
|
"fontWeight": "400",
|
|
79
|
-
"lineHeight":
|
|
79
|
+
"lineHeight": 24,
|
|
80
80
|
},
|
|
81
81
|
Object {
|
|
82
82
|
"color": "hsl(130, 27%, 29%)",
|
|
@@ -12,9 +12,9 @@ export const getThemedStyles = (theme: StrictTheme): LabelStyles => {
|
|
|
12
12
|
|
|
13
13
|
return StyleSheet.create({
|
|
14
14
|
text: {
|
|
15
|
-
fontSize: fontSizes.
|
|
15
|
+
fontSize: fontSizes.medium,
|
|
16
16
|
fontWeight: fontWeights.normal,
|
|
17
|
-
lineHeight: getLineHeight(fontSizes.
|
|
17
|
+
lineHeight: getLineHeight(fontSizes.medium),
|
|
18
18
|
...components?.label?.text,
|
|
19
19
|
},
|
|
20
20
|
primary: {
|