@react-native-ama/forms 1.0.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.
- package/dist/core/src/components/AMAProvider.d.ts +26 -0
- package/dist/core/src/components/AMAProvider.js +147 -0
- package/dist/core/src/components/AutofocusContainer.d.ts +9 -0
- package/dist/core/src/components/AutofocusContainer.js +58 -0
- package/dist/core/src/components/HideChildrenFromAccessibilityTree.d.ts +6 -0
- package/dist/core/src/components/HideChildrenFromAccessibilityTree.js +40 -0
- package/dist/core/src/hooks/useButtonChecks.d.ts +6 -0
- package/dist/core/src/hooks/useButtonChecks.js +51 -0
- package/dist/core/src/hooks/useChecks.d.ts +15 -0
- package/dist/core/src/hooks/useChecks.js +152 -0
- package/dist/core/src/hooks/useFocus.d.ts +4 -0
- package/dist/core/src/hooks/useFocus.js +55 -0
- package/dist/core/src/hooks/useTimedAction.d.ts +3 -0
- package/dist/core/src/hooks/useTimedAction.js +63 -0
- package/dist/core/src/index.d.ts +7 -0
- package/dist/core/src/index.js +20 -0
- package/dist/forms/src/components/Form.d.ts +24 -0
- package/dist/forms/src/components/Form.js +122 -0
- package/dist/forms/src/components/FormField.d.ts +7 -0
- package/dist/forms/src/components/FormField.js +52 -0
- package/dist/forms/src/components/FormSubmit.d.ts +10 -0
- package/dist/forms/src/components/FormSubmit.js +25 -0
- package/dist/forms/src/components/FormSwitch.d.ts +4 -0
- package/dist/forms/src/components/FormSwitch.js +15 -0
- package/dist/forms/src/components/TextInput.d.ts +26 -0
- package/dist/forms/src/components/TextInput.js +123 -0
- package/dist/forms/src/hooks/useFormField.d.ts +26 -0
- package/dist/forms/src/hooks/useFormField.js +92 -0
- package/dist/forms/src/hooks/useTextInput.d.ts +350 -0
- package/dist/forms/src/hooks/useTextInput.js +102 -0
- package/dist/forms/src/index.d.ts +16 -0
- package/dist/forms/src/index.js +24 -0
- package/dist/internal/src/checks/checkAccessibilityRole.d.ts +3 -0
- package/dist/internal/src/checks/checkAccessibilityRole.js +32 -0
- package/dist/internal/src/checks/checkFocusTrap.d.ts +8 -0
- package/dist/internal/src/checks/checkFocusTrap.js +34 -0
- package/dist/internal/src/checks/checkForAccessibilityState.d.ts +7 -0
- package/dist/internal/src/checks/checkForAccessibilityState.js +48 -0
- package/dist/internal/src/checks/checkMinimumSize.d.ts +3 -0
- package/dist/internal/src/checks/checkMinimumSize.js +29 -0
- package/dist/internal/src/checks/contrastChecker.d.ts +8 -0
- package/dist/internal/src/checks/contrastChecker.js +77 -0
- package/dist/internal/src/checks/noUndefinedProperty.d.ts +8 -0
- package/dist/internal/src/checks/noUndefinedProperty.js +14 -0
- package/dist/internal/src/checks/uppercaseChecker.d.ts +8 -0
- package/dist/internal/src/checks/uppercaseChecker.js +18 -0
- package/dist/internal/src/checks/uppercaseStringChecker.d.ts +8 -0
- package/dist/internal/src/checks/uppercaseStringChecker.js +29 -0
- package/dist/internal/src/index.d.ts +20 -0
- package/dist/internal/src/index.js +56 -0
- package/dist/internal/src/types.d.ts +17 -0
- package/dist/internal/src/types.js +2 -0
- package/dist/internal/src/utils/applyStyle.d.ts +9 -0
- package/dist/internal/src/utils/applyStyle.js +33 -0
- package/dist/internal/src/utils/constants.d.ts +3 -0
- package/dist/internal/src/utils/constants.js +32 -0
- package/dist/internal/src/utils/error.style.d.ts +6 -0
- package/dist/internal/src/utils/error.style.js +11 -0
- package/dist/internal/src/utils/generateAccessibilityStateFromProp.d.ts +9 -0
- package/dist/internal/src/utils/generateAccessibilityStateFromProp.js +35 -0
- package/dist/internal/src/utils/getPropertyFromStyle.d.ts +2 -0
- package/dist/internal/src/utils/getPropertyFromStyle.js +10 -0
- package/dist/internal/src/utils/interpolateAnimationStates.d.ts +2 -0
- package/dist/internal/src/utils/interpolateAnimationStates.js +43 -0
- package/dist/internal/src/utils/isFocused.d.ts +2 -0
- package/dist/internal/src/utils/isFocused.js +7 -0
- package/dist/internal/src/utils/logger.d.ts +16 -0
- package/dist/internal/src/utils/logger.js +53 -0
- package/dist/internal/src/utils/logger.rules.d.ts +15 -0
- package/dist/internal/src/utils/logger.rules.js +74 -0
- package/dist/internal/src/utils/maybeGenerateStringFromElement.d.ts +2 -0
- package/dist/internal/src/utils/maybeGenerateStringFromElement.js +51 -0
- package/dist/internal/src/utils/minimumTouchableSize.d.ts +3 -0
- package/dist/internal/src/utils/minimumTouchableSize.js +9 -0
- package/dist/react-native/src/components/ExpandablePressable.d.ts +6 -0
- package/dist/react-native/src/components/ExpandablePressable.js +26 -0
- package/dist/react-native/src/components/Pressable.d.ts +5 -0
- package/dist/react-native/src/components/Pressable.js +49 -0
- package/dist/react-native/src/components/SwitchListItem.d.ts +12 -0
- package/dist/react-native/src/components/SwitchListItem.js +44 -0
- package/dist/react-native/src/components/SwitchWrapper.d.ts +11 -0
- package/dist/react-native/src/components/SwitchWrapper.js +36 -0
- package/dist/react-native/src/components/Text.d.ts +6 -0
- package/dist/react-native/src/components/Text.js +77 -0
- package/dist/react-native/src/components/TouchableOpacity.d.ts +6 -0
- package/dist/react-native/src/components/TouchableOpacity.js +48 -0
- package/dist/react-native/src/components/TouchableWithoutFeedback.d.ts +5 -0
- package/dist/react-native/src/components/TouchableWithoutFeedback.js +48 -0
- package/dist/react-native/src/hooks/useExpandable.d.ts +13 -0
- package/dist/react-native/src/hooks/useExpandable.js +31 -0
- package/dist/react-native/src/hooks/usePressable.d.ts +16 -0
- package/dist/react-native/src/hooks/usePressable.js +44 -0
- package/dist/react-native/src/hooks/useSwitch.d.ts +10 -0
- package/dist/react-native/src/hooks/useSwitch.js +25 -0
- package/dist/react-native/src/index.d.ts +10 -0
- package/dist/react-native/src/index.js +25 -0
- package/package.json +82 -0
- package/src/components/Form.test.tsx +262 -0
- package/src/components/Form.tsx +138 -0
- package/src/components/FormField.test.tsx +54 -0
- package/src/components/FormField.tsx +45 -0
- package/src/components/FormSubmit.test.tsx +71 -0
- package/src/components/FormSubmit.tsx +43 -0
- package/src/components/FormSwitch.tsx +17 -0
- package/src/components/TextInput.test.tsx +540 -0
- package/src/components/TextInput.tsx +168 -0
- package/src/hooks/useFormField.test.tsx +183 -0
- package/src/hooks/useFormField.ts +133 -0
- package/src/hooks/useTextInput.ts +128 -0
- package/src/index.ts +39 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as AMAProvider from '@react-native-ama/core';
|
|
2
|
+
import { renderHook } from '@testing-library/react-native';
|
|
3
|
+
import { waitFor } from '@testing-library/react-native';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
|
|
6
|
+
import * as Form from '../components/Form';
|
|
7
|
+
import { useFormField } from './useFormField';
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
let submitForm: jest.Mock = jest.fn();
|
|
14
|
+
|
|
15
|
+
describe('useFormField', () => {
|
|
16
|
+
let localRef: any[] = [];
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
jest.spyOn(AMAProvider, 'useAMAContext').mockReturnValue({
|
|
20
|
+
isScreenReaderEnabled: true,
|
|
21
|
+
} as any);
|
|
22
|
+
|
|
23
|
+
jest.spyOn(Form, 'useForm').mockImplementation(() => {
|
|
24
|
+
return { refs: localRef, submitForm };
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(function () {
|
|
29
|
+
localRef.splice(-1);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('register itself as ref when is mounted', async () => {
|
|
33
|
+
renderHook(
|
|
34
|
+
() => {
|
|
35
|
+
return useFormField({
|
|
36
|
+
ref: { current: 'hello' },
|
|
37
|
+
hasFocusCallback: false,
|
|
38
|
+
id: 'my-id',
|
|
39
|
+
nextFieldId: 'next-field-id',
|
|
40
|
+
nextFormFieldRef: { current: 'nextFormFieldRef' },
|
|
41
|
+
hasValidation: false,
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
{ wrapper: renderHookWrapper },
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
await waitFor(() => {
|
|
48
|
+
expect(localRef).toHaveLength(1);
|
|
49
|
+
expect(localRef[0]).toEqual({
|
|
50
|
+
hasFocusCallback: false,
|
|
51
|
+
ref: { current: { current: 'hello' } },
|
|
52
|
+
id: 'my-id',
|
|
53
|
+
hasError: undefined,
|
|
54
|
+
hasValidation: false,
|
|
55
|
+
isEditable: true,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('unregister itself from the refs when is mounted', () => {
|
|
61
|
+
const { unmount } = renderHook(
|
|
62
|
+
() => {
|
|
63
|
+
return useFormField({
|
|
64
|
+
ref: { current: 'hello' },
|
|
65
|
+
hasFocusCallback: false,
|
|
66
|
+
hasValidation: false,
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
{ wrapper: renderHookWrapper },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
unmount();
|
|
73
|
+
|
|
74
|
+
expect(localRef.length).toBe(0);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('focusNextFormField', () => {
|
|
78
|
+
describe('Given the form field is not the last of the refs', () => {
|
|
79
|
+
describe('checks for focus when calling focusNextFormField', function () {
|
|
80
|
+
it('checks only against the current field if next one does not have the focus callback', async () => {
|
|
81
|
+
const focus = jest.fn();
|
|
82
|
+
const ref = { current: { focus, isFocused: jest.fn() } };
|
|
83
|
+
|
|
84
|
+
const { result } = renderHook(
|
|
85
|
+
() => {
|
|
86
|
+
const first = useFormField({
|
|
87
|
+
ref: { current: 'random ref' },
|
|
88
|
+
hasFocusCallback: true,
|
|
89
|
+
hasValidation: false,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// next field
|
|
93
|
+
useFormField({
|
|
94
|
+
ref,
|
|
95
|
+
hasFocusCallback: false,
|
|
96
|
+
hasValidation: false,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return first;
|
|
100
|
+
},
|
|
101
|
+
{ wrapper: renderHookWrapper },
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
result.current.focusNextFormField();
|
|
105
|
+
|
|
106
|
+
await waitFor(() =>
|
|
107
|
+
expect(checkFocusTrap).toHaveBeenCalledWith({
|
|
108
|
+
ref: { current: 'random ref' },
|
|
109
|
+
shouldHaveFocus: false,
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(checkFocusTrap).toHaveBeenCalledTimes(1);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('triggers submitForm when the element is the last one of the ref', async () => {
|
|
119
|
+
jest.spyOn(AMAProvider, 'useAMAContext').mockReturnValue({
|
|
120
|
+
isScreenReaderEnabled: true,
|
|
121
|
+
} as any);
|
|
122
|
+
|
|
123
|
+
const ref = { current: 'whatever' };
|
|
124
|
+
|
|
125
|
+
const { result } = renderHook(
|
|
126
|
+
() => {
|
|
127
|
+
useFormField({
|
|
128
|
+
ref: { current: 'random ref' },
|
|
129
|
+
hasFocusCallback: false,
|
|
130
|
+
hasValidation: false,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// next field
|
|
134
|
+
return useFormField({
|
|
135
|
+
ref,
|
|
136
|
+
hasFocusCallback: false,
|
|
137
|
+
hasValidation: false,
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
{ wrapper: renderHookWrapper },
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
result.current.focusNextFormField();
|
|
145
|
+
|
|
146
|
+
expect(submitForm).toHaveBeenCalledWith();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
let setFocus: jest.Mock;
|
|
153
|
+
let checkFocusTrap: jest.Mock;
|
|
154
|
+
|
|
155
|
+
function mockAMACore() {
|
|
156
|
+
const original = jest.requireActual('@react-native-ama/core');
|
|
157
|
+
|
|
158
|
+
setFocus = jest.fn();
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
...original,
|
|
162
|
+
useFocus: () => {
|
|
163
|
+
return {
|
|
164
|
+
setFocus,
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function mockAMAInternal() {
|
|
171
|
+
const original = jest.requireActual('@react-native-ama/internal');
|
|
172
|
+
|
|
173
|
+
checkFocusTrap = jest.fn().mockResolvedValue(null);
|
|
174
|
+
|
|
175
|
+
return { ...original, checkFocusTrap };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const renderHookWrapper = ({ children }) => (
|
|
179
|
+
<AMAProvider.AMAProvider>{children}</AMAProvider.AMAProvider>
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
jest.mock('@react-native-ama/core', () => mockAMACore());
|
|
183
|
+
jest.mock('@react-native-ama/internal', () => mockAMAInternal());
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useChecks } from '@react-native-ama/core';
|
|
2
|
+
import { applyStyle } from '@react-native-ama/internal';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Keyboard, ViewStyle } from 'react-native';
|
|
5
|
+
|
|
6
|
+
import { useForm } from '../components/Form';
|
|
7
|
+
|
|
8
|
+
export type UseFormField = {
|
|
9
|
+
ref?: React.RefObject<any> | React.ForwardedRef<any> | null;
|
|
10
|
+
hasFocusCallback: boolean;
|
|
11
|
+
id?: string;
|
|
12
|
+
nextFieldId?: string;
|
|
13
|
+
nextFormFieldRef?: React.RefObject<any>;
|
|
14
|
+
accessibilityHint?: string;
|
|
15
|
+
style?: ViewStyle;
|
|
16
|
+
editable?: boolean;
|
|
17
|
+
hasValidation: boolean;
|
|
18
|
+
hasError?: boolean;
|
|
19
|
+
errorMessage?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const useFormField = ({
|
|
23
|
+
ref,
|
|
24
|
+
hasFocusCallback,
|
|
25
|
+
id,
|
|
26
|
+
nextFieldId,
|
|
27
|
+
nextFormFieldRef,
|
|
28
|
+
hasValidation,
|
|
29
|
+
hasError,
|
|
30
|
+
accessibilityHint,
|
|
31
|
+
errorMessage,
|
|
32
|
+
editable = true,
|
|
33
|
+
style = {},
|
|
34
|
+
}: UseFormField) => {
|
|
35
|
+
const { refs, submitForm, focusField } = useForm();
|
|
36
|
+
const fieldRef = React.useRef(ref);
|
|
37
|
+
|
|
38
|
+
const checks = __DEV__ ? useChecks?.() : undefined;
|
|
39
|
+
|
|
40
|
+
const getMyIndex = () => {
|
|
41
|
+
const allRefs = refs!;
|
|
42
|
+
const item = allRefs.find(r => r.ref === fieldRef);
|
|
43
|
+
|
|
44
|
+
return item ? allRefs.indexOf(item) : -1;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const focusNextFormField = () => {
|
|
48
|
+
const isLastItem = isLastField();
|
|
49
|
+
|
|
50
|
+
if (isLastItem) {
|
|
51
|
+
Keyboard.dismiss();
|
|
52
|
+
|
|
53
|
+
submitForm();
|
|
54
|
+
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const currentField = refs![getMyIndex()];
|
|
59
|
+
|
|
60
|
+
focusField?.(getNextFieldRef());
|
|
61
|
+
|
|
62
|
+
__DEV__ &&
|
|
63
|
+
currentField?.hasFocusCallback &&
|
|
64
|
+
checks?.checkFocusTrap({
|
|
65
|
+
ref: currentField?.ref?.current,
|
|
66
|
+
shouldHaveFocus: false,
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getNextFieldRef = () => {
|
|
71
|
+
const allRefs = refs || [];
|
|
72
|
+
|
|
73
|
+
if (nextFormFieldRef) {
|
|
74
|
+
return {
|
|
75
|
+
ref: nextFormFieldRef,
|
|
76
|
+
};
|
|
77
|
+
} else if (nextFieldId) {
|
|
78
|
+
return allRefs.find(item => item.id === nextFieldId);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return allRefs[getMyIndex() + 1];
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const isLastField = () => {
|
|
85
|
+
const myIndex = getMyIndex();
|
|
86
|
+
|
|
87
|
+
return myIndex === refs!.length - 1;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
React.useEffect(() => {
|
|
91
|
+
refs?.push({
|
|
92
|
+
ref: fieldRef,
|
|
93
|
+
hasFocusCallback,
|
|
94
|
+
id,
|
|
95
|
+
hasValidation,
|
|
96
|
+
hasError,
|
|
97
|
+
isEditable: editable,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
const myIndex = getMyIndex();
|
|
102
|
+
|
|
103
|
+
refs?.splice(myIndex, 1);
|
|
104
|
+
};
|
|
105
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
108
|
+
React.useEffect(() => {
|
|
109
|
+
const myRefIndex = refs?.findIndex(item => item.ref === fieldRef);
|
|
110
|
+
|
|
111
|
+
// @ts-ignore
|
|
112
|
+
refs[myRefIndex].hasError = hasError;
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
refs[myRefIndex].isEditable = editable;
|
|
115
|
+
}, [editable, hasError, refs]);
|
|
116
|
+
|
|
117
|
+
const fullAccessibilityHint = [accessibilityHint, errorMessage]
|
|
118
|
+
.filter(Boolean)
|
|
119
|
+
.join(',');
|
|
120
|
+
|
|
121
|
+
return __DEV__
|
|
122
|
+
? {
|
|
123
|
+
focusNextFormField,
|
|
124
|
+
isLastField,
|
|
125
|
+
style: applyStyle?.({ style, debugStyle: checks?.debugStyle }),
|
|
126
|
+
accessibilityHint: fullAccessibilityHint,
|
|
127
|
+
}
|
|
128
|
+
: {
|
|
129
|
+
focusNextFormField,
|
|
130
|
+
accessibilityHint: fullAccessibilityHint,
|
|
131
|
+
isLastField,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useChecks } from '@react-native-ama/core';
|
|
2
|
+
import { applyStyle } from '@react-native-ama/internal';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import type {
|
|
5
|
+
LayoutChangeEvent,
|
|
6
|
+
NativeSyntheticEvent,
|
|
7
|
+
ReturnKeyTypeOptions,
|
|
8
|
+
StyleProp,
|
|
9
|
+
TextInputSubmitEditingEventData,
|
|
10
|
+
TextStyle,
|
|
11
|
+
ViewStyle,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
|
|
14
|
+
import { TextInputProps } from '../components/TextInput';
|
|
15
|
+
import { UseFormField, useFormField } from './useFormField';
|
|
16
|
+
|
|
17
|
+
type UseTextInput = Omit<UseFormField, 'hasFocusCallback'> & {
|
|
18
|
+
onLayout?: (event: LayoutChangeEvent) => void;
|
|
19
|
+
returnKeyType?: ReturnKeyTypeOptions;
|
|
20
|
+
onSubmitEditing?: (
|
|
21
|
+
event: NativeSyntheticEvent<TextInputSubmitEditingEventData>,
|
|
22
|
+
) => void;
|
|
23
|
+
accessibilityLabel: string;
|
|
24
|
+
editable?: boolean;
|
|
25
|
+
style?: ViewStyle | StyleProp<TextStyle> | null;
|
|
26
|
+
required: boolean;
|
|
27
|
+
requiredMessage?: string;
|
|
28
|
+
hasValidation: boolean;
|
|
29
|
+
hasError?: boolean;
|
|
30
|
+
errorMessage?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const useTextInput = ({
|
|
34
|
+
onLayout,
|
|
35
|
+
returnKeyType,
|
|
36
|
+
onSubmitEditing,
|
|
37
|
+
accessibilityLabel,
|
|
38
|
+
requiredMessage,
|
|
39
|
+
required,
|
|
40
|
+
...rest
|
|
41
|
+
}: UseTextInput) => {
|
|
42
|
+
const [theReturnKeyType, setTheReturnKeyType] = React.useState(
|
|
43
|
+
returnKeyType || 'next',
|
|
44
|
+
);
|
|
45
|
+
const { style: formFieldStyle, ...formField } = useFormField({
|
|
46
|
+
hasFocusCallback: true,
|
|
47
|
+
...rest,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const handleOnSubmitEditing = (
|
|
51
|
+
event: NativeSyntheticEvent<TextInputSubmitEditingEventData>,
|
|
52
|
+
) => {
|
|
53
|
+
onSubmitEditing?.(event);
|
|
54
|
+
|
|
55
|
+
formField.focusNextFormField();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const checkReturnKeyType = (event: LayoutChangeEvent) => {
|
|
59
|
+
const setReturnKeyTypeAsDone =
|
|
60
|
+
formField.isLastField() && returnKeyType == null;
|
|
61
|
+
|
|
62
|
+
if (setReturnKeyTypeAsDone) {
|
|
63
|
+
setTheReturnKeyType('done');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onLayout?.(event);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const checks = __DEV__ ? useChecks?.() : undefined;
|
|
70
|
+
|
|
71
|
+
__DEV__ &&
|
|
72
|
+
checks?.noUndefinedProperty({
|
|
73
|
+
properties: { accessibilityLabel },
|
|
74
|
+
property: 'accessibilityLabel',
|
|
75
|
+
rule: 'NO_FORM_LABEL',
|
|
76
|
+
});
|
|
77
|
+
__DEV__ &&
|
|
78
|
+
checks?.noUppercaseStringChecker({
|
|
79
|
+
text: accessibilityLabel,
|
|
80
|
+
});
|
|
81
|
+
__DEV__ &&
|
|
82
|
+
checks?.noUppercaseStringChecker({
|
|
83
|
+
text: rest.accessibilityHint,
|
|
84
|
+
});
|
|
85
|
+
__DEV__ &&
|
|
86
|
+
accessibilityLabel?.trim().slice(-1) === '*' &&
|
|
87
|
+
checks?.logResult('NO_FORM_LABEL_ENDING_WITH_ASTERISK', {
|
|
88
|
+
rule: 'NO_FORM_LABEL_ENDING_WITH_ASTERISK',
|
|
89
|
+
message:
|
|
90
|
+
'Form labels should not end with an asterisk. Please remove it and provide a required message instead.',
|
|
91
|
+
});
|
|
92
|
+
__DEV__ &&
|
|
93
|
+
required &&
|
|
94
|
+
checks?.noUndefinedProperty({
|
|
95
|
+
properties: { requiredMessage },
|
|
96
|
+
property: 'requiredMessage',
|
|
97
|
+
rule: 'NO_FORM_LABEL_ENDING_WITH_ASTERISK',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const fullAccessibilityLabel = required
|
|
101
|
+
? `${accessibilityLabel}, ${requiredMessage}`
|
|
102
|
+
: accessibilityLabel;
|
|
103
|
+
|
|
104
|
+
const advancedProps: Partial<TextInputProps> = {
|
|
105
|
+
accessibilityLabel: fullAccessibilityLabel,
|
|
106
|
+
returnKeyType: theReturnKeyType,
|
|
107
|
+
onSubmitEditing: handleOnSubmitEditing,
|
|
108
|
+
onLayout: checkReturnKeyType,
|
|
109
|
+
blurOnSubmit: theReturnKeyType === 'done',
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return __DEV__
|
|
113
|
+
? {
|
|
114
|
+
...rest,
|
|
115
|
+
...formField,
|
|
116
|
+
...advancedProps,
|
|
117
|
+
style: applyStyle?.({
|
|
118
|
+
// @ts-ignore
|
|
119
|
+
style: formFieldStyle,
|
|
120
|
+
debugStyle: checks?.debugStyle,
|
|
121
|
+
}) as any,
|
|
122
|
+
}
|
|
123
|
+
: {
|
|
124
|
+
...rest,
|
|
125
|
+
...formField,
|
|
126
|
+
...advancedProps,
|
|
127
|
+
};
|
|
128
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Components
|
|
2
|
+
import {
|
|
3
|
+
FormActions,
|
|
4
|
+
FormProps,
|
|
5
|
+
Form as FormProvider,
|
|
6
|
+
} from './components/Form';
|
|
7
|
+
import { FormField, FormFieldProps } from './components/FormField';
|
|
8
|
+
import { FormSubmit, FormSubmitProps } from './components/FormSubmit';
|
|
9
|
+
import { FormSwitch, FormSwitchProps } from './components/FormSwitch';
|
|
10
|
+
|
|
11
|
+
type FormComponent = React.FunctionComponent<FormProps> & {
|
|
12
|
+
Submit: (props: FormSubmitProps) => JSX.Element;
|
|
13
|
+
Field: React.FunctionComponent<FormFieldProps>;
|
|
14
|
+
Switch: (props: FormSwitchProps) => JSX.Element;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
const Form: FormComponent = FormProvider;
|
|
19
|
+
Form.Submit = FormSubmit;
|
|
20
|
+
Form.Field = FormField;
|
|
21
|
+
Form.Switch = FormSwitch;
|
|
22
|
+
|
|
23
|
+
export { Form, FormField, FormSubmit };
|
|
24
|
+
|
|
25
|
+
// Components
|
|
26
|
+
export { TextInput } from './components/TextInput';
|
|
27
|
+
|
|
28
|
+
// Hooks
|
|
29
|
+
export { useFormField } from './hooks/useFormField';
|
|
30
|
+
export { useTextInput } from './hooks/useTextInput';
|
|
31
|
+
|
|
32
|
+
// Types
|
|
33
|
+
export {
|
|
34
|
+
type FormProps,
|
|
35
|
+
type FormActions,
|
|
36
|
+
type FormFieldProps,
|
|
37
|
+
type FormSubmitProps,
|
|
38
|
+
type FormSwitchProps,
|
|
39
|
+
};
|