Skip to content

Commit caabd21

Browse files
authored
expose an imperative validate method on Incubator.TextField component (#1459)
* expose an imperative validate method on Incubator.TextField component * Update example screen imperative validation example
1 parent 835dfef commit caabd21

File tree

9 files changed

+63
-6
lines changed

9 files changed

+63
-6
lines changed

demo/src/screens/incubatorScreens/IncubatorTextFieldScreen.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {KeyboardAwareInsetsView} = Keyboard;
88
export default class TextFieldScreen extends Component {
99
input = React.createRef<TextInput>();
1010
input2 = React.createRef<TextInput>();
11+
inputWithValidation = React.createRef<TextInput>();
1112
state = {
1213
errorPosition: TextField.validationMessagePositions.TOP,
1314
shouldDisable: false,
@@ -121,6 +122,7 @@ export default class TextFieldScreen extends Component {
121122
<Text h3 blue50>
122123
Validation
123124
</Text>
125+
124126
<Button
125127
size={Button.sizes.xSmall}
126128
label={`Error Position: ${_.upperCase(errorPosition)}`}
@@ -150,8 +152,28 @@ export default class TextFieldScreen extends Component {
150152
// validateOnStart
151153
// validateOnBlur
152154
fieldStyle={styles.withUnderline}
155+
marginB-s4
153156
/>
154157

158+
<View row top marginT-s4>
159+
<TextField
160+
ref={this.inputWithValidation}
161+
placeholder="Enter full name"
162+
validate="required"
163+
validationMessage="This field is required"
164+
containerStyle={{flexGrow: 1}}
165+
fieldStyle={styles.withUnderline}
166+
/>
167+
<Button
168+
marginL-s5
169+
label="Validate"
170+
size={Button.sizes.xSmall}
171+
onPress={() => {
172+
this.inputWithValidation.current?.validate?.();
173+
}}
174+
/>
175+
</View>
176+
155177
<View row centerV spread>
156178
<Text h3 blue50 marginV-s4>
157179
Colors By State

generatedTypes/incubator/TextField/FieldContext.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export declare type FieldContextType = {
66
isValid: boolean;
77
failingValidatorIndex?: number;
88
disabled: boolean;
9+
validateField: () => void;
910
};
1011
declare const FieldContext: import("react").Context<FieldContextType>;
1112
export default FieldContext;

generatedTypes/incubator/TextField/useFieldState.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ export default function useFieldState({ validate, validateOnBlur, validateOnChan
2424
isFocused: boolean;
2525
failingValidatorIndex: number | undefined;
2626
};
27+
validateField: (valueToValidate?: any) => void;
2728
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import React from 'react';
2+
import { TextInput } from 'react-native';
3+
declare const useImperativeInputHandle: (ref: React.Ref<any>) => React.MutableRefObject<TextInput | undefined>;
4+
export default useImperativeInputHandle;

src/incubator/TextField/FieldContext.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import _ from 'lodash';
12
import {createContext} from 'react';
23

34
export type FieldContextType = {
@@ -7,14 +8,16 @@ export type FieldContextType = {
78
isValid: boolean;
89
failingValidatorIndex?: number;
910
disabled: boolean;
11+
validateField: () => void
1012
};
1113

1214
const FieldContext = createContext<FieldContextType>({
1315
isFocused: false,
1416
hasValue: false,
1517
isValid: true,
1618
failingValidatorIndex: undefined,
17-
disabled: false
19+
disabled: false,
20+
validateField: _.noop
1821
});
1922

2023
export default FieldContext;

src/incubator/TextField/Input.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {getColorByState} from './Presenter';
66
import {Colors} from '../../style';
77
import {Constants} from '../../helpers';
88
import FieldContext from './FieldContext';
9+
import useImperativeInputHandle from './useImperativeInputHandle';
910

1011
const DEFAULT_INPUT_COLOR: ColorType = {
1112
default: Colors.grey10,
@@ -35,6 +36,7 @@ const Input = ({
3536
forwardedRef,
3637
...props
3738
}: InputProps & ForwardRefInjectedProps) => {
39+
const inputRef = useImperativeInputHandle(forwardedRef);
3840
const context = useContext(FieldContext);
3941
const placeholder = !context.isFocused ? props.placeholder : hint || props.placeholder;
4042
const inputColor = getColorByState(color, context);
@@ -46,7 +48,8 @@ const Input = ({
4648
{...props}
4749
placeholder={placeholder}
4850
placeholderTextColor={placeholderTextColor}
49-
ref={forwardedRef}
51+
// @ts-expect-error
52+
ref={inputRef}
5053
underlineColorAndroid="transparent"
5154
accessibilityState={{disabled: props.editable === false}}
5255
/>

src/incubator/TextField/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ const TextField = (props: InternalTextFieldProps) => {
142142
placeholder,
143143
...others
144144
} = usePreset(props);
145-
const {onFocus, onBlur, onChangeText, fieldState} = useFieldState(others);
145+
const {onFocus, onBlur, onChangeText, fieldState, validateField} = useFieldState(others);
146146

147147
const context = useMemo(() => {
148-
return {...fieldState, disabled: others.editable === false};
149-
}, [fieldState, others.editable]);
148+
return {...fieldState, disabled: others.editable === false, validateField};
149+
}, [fieldState, others.editable, validateField]);
150150

151151
const {margins, paddings, typography, color} = modifiers;
152152
const typographyStyle = useMemo(() => omit(typography, 'lineHeight'), [typography]);

src/incubator/TextField/useFieldState.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export default function useFieldState({
9393
onFocus,
9494
onBlur,
9595
onChangeText,
96-
fieldState
96+
fieldState,
97+
validateField
9798
};
9899
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React, {useContext, useImperativeHandle, useRef} from 'react';
2+
import {TextInput} from 'react-native';
3+
import FieldContext from './FieldContext';
4+
5+
const useImperativeInputHandle = (ref: React.Ref<any>) => {
6+
const inputRef = useRef<TextInput>();
7+
const context = useContext(FieldContext);
8+
useImperativeHandle(ref, () => {
9+
return {
10+
focus: () => inputRef.current?.focus(),
11+
blur: () => inputRef.current?.blur(),
12+
clear: () => inputRef.current?.clear(),
13+
validate: () => {
14+
context.validateField();
15+
}
16+
};
17+
});
18+
19+
return inputRef;
20+
};
21+
22+
export default useImperativeInputHandle;

0 commit comments

Comments
 (0)