Skip to content

centralized all textfield types in a single file #1933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions src/incubator/TextField/CharCounter.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
import React, {useContext} from 'react';
import {TextStyle, StyleSheet} from 'react-native';
import {StyleSheet} from 'react-native';
import _ from 'lodash';
import Text from '../../components/text';
import FieldContext from './FieldContext';

export interface CharCounterProps {
/**
* Should show a character counter (works only with maxLength)
*/
showCharCounter?: boolean;
maxLength?: number;
/**
* Pass custom style to character counter text
*/
charCounterStyle?: TextStyle;
testID: string;
}
import {CharCounterProps} from './types';

const CharCounter = ({maxLength, charCounterStyle, testID}: CharCounterProps) => {
const {value} = useContext(FieldContext);
Expand Down
11 changes: 1 addition & 10 deletions src/incubator/TextField/FieldContext.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import _ from 'lodash';
import {createContext} from 'react';

export type FieldContextType = {
value?: string;
isFocused: boolean;
hasValue: boolean;
isValid: boolean;
failingValidatorIndex?: number;
disabled: boolean;
validateField: () => void
};
import {FieldContextType} from './types';

const FieldContext = createContext<FieldContextType>({
isFocused: false,
Expand Down
26 changes: 2 additions & 24 deletions src/incubator/TextField/FloatingPlaceholder.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,13 @@
import React, {useContext, useEffect, useRef, useCallback, useState, useMemo} from 'react';
import {Animated, LayoutChangeEvent, StyleSheet, Platform, TextStyle, StyleProp} from 'react-native';
import {ColorType, ValidationMessagePosition} from './types';
import {Animated, LayoutChangeEvent, StyleSheet, Platform} from 'react-native';
import {FloatingPlaceholderProps, ValidationMessagePosition} from './types';
import {getColorByState} from './Presenter';
import {Colors} from '../../style';
import {Constants} from '../../commons/new';
import View from '../../components/view';
import Text from '../../components/text';
import FieldContext from './FieldContext';

export interface FloatingPlaceholderProps {
/**
* The placeholder for the field
*/
placeholder?: string;
/**
* The floating placeholder color
*/
floatingPlaceholderColor?: ColorType;
/**
* Custom style to pass to the floating placeholder
*/
floatingPlaceholderStyle?: StyleProp<TextStyle>;
/**
* Should placeholder float on focus or when start typing
*/
floatOnFocus?: boolean;
validationMessagePosition?: ValidationMessagePosition;
extraOffset?: number;
testID: string;
}

const FLOATING_PLACEHOLDER_SCALE = 0.875;

const FloatingPlaceholder = ({
Expand Down
24 changes: 2 additions & 22 deletions src/incubator/TextField/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {useContext} from 'react';
import {TextInput, TextInputProps, StyleSheet, Platform} from 'react-native';
import {TextInput, StyleSheet, Platform} from 'react-native';
import {Constants, ForwardRefInjectedProps} from '../../commons/new';
import {ColorType} from './types';
import {InputProps, ColorType} from './types';
import {getColorByState} from './Presenter';
import {Colors} from '../../style';
import FieldContext from './FieldContext';
Expand All @@ -11,26 +11,6 @@ const DEFAULT_INPUT_COLOR: ColorType = {
default: Colors.$textDefault,
disabled: Colors.$textDisabled
};
export interface InputProps
extends Omit<TextInputProps, 'placeholderTextColor'>,
Omit<React.ComponentPropsWithRef<typeof TextInput>, 'placeholderTextColor'> {
/**
* A hint text to display when focusing the field
*/
hint?: string;
/**
* Input color
*/
color?: ColorType;
/**
* placeholder text color
*/
placeholderTextColor?: ColorType;
/**
* Custom formatter for the input value (used only when input if not focused)
*/
formatter?: (value?: string) => string | undefined;
}

const Input = ({
style,
Expand Down
27 changes: 3 additions & 24 deletions src/incubator/TextField/Label.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
import React, {useContext} from 'react';
import {StyleSheet, TextStyle} from 'react-native';
import {StyleSheet} from 'react-native';
import {Colors} from '../../style';
import Text, {TextProps} from '../../components/text';
import {ColorType, ValidationMessagePosition} from './types';
import Text from '../../components/text';
import {LabelProps, ValidationMessagePosition} from './types';
import {getColorByState} from './Presenter';
import FieldContext from './FieldContext';

export interface LabelProps {
/**
* Field label
*/
label?: string;
/**
* Field label color. Either a string or color by state map ({default, focus, error, disabled})
*/
labelColor?: ColorType;
/**
* Custom style for the field label
*/
labelStyle?: TextStyle;
/**
* Pass extra props to the label Text element
*/
labelProps?: TextProps;
validationMessagePosition?: ValidationMessagePosition;
floatingPlaceholder?: boolean;
testID?: string;
}

const Label = ({
label,
Expand Down
3 changes: 1 addition & 2 deletions src/incubator/TextField/Presenter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import _ from 'lodash';
import {Colors} from './../../style';
import {FieldContextType} from './FieldContext';
import {ColorType, Validator} from './types';
import {ColorType, Validator, FieldContextType} from './types';
// TODO: Fix this import after moving all TextField types to a single file after we move to the new docs
import {TextFieldProps} from './index';
import formValidators from './validators';
Expand Down
22 changes: 2 additions & 20 deletions src/incubator/TextField/ValidationMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,9 @@
import React, {useContext} from 'react';
import {TextStyle, StyleSheet} from 'react-native';
import {StyleSheet} from 'react-native';
import Text from '../../components/text';
import FieldContext from './FieldContext';
import {getRelevantValidationMessage} from './Presenter';
import {FieldStateProps} from './useFieldState';

export interface ValidationMessageProps {
/**
* Should support showing validation error message
*/
enableErrors?: boolean;
/**
* The validation message to display when field is invalid (depends on validate)
*/
validationMessage?: string | string[];
/**
* Custom style for the validation message
*/
validationMessageStyle?: TextStyle;
retainSpace?: boolean;
validate?: FieldStateProps['validate'];
testID?: string;
}
import {ValidationMessageProps} from './types';

const ValidationMessage = ({
validationMessage,
Expand Down
120 changes: 18 additions & 102 deletions src/incubator/TextField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,113 +5,22 @@
* 3. Passing typography preset that includes lineHeight usually cause alignment issues with
* other elements (leading/trailing accessories). It usually best to set lineHeight with undefined
*/
import React, {PropsWithChildren, ReactElement, useMemo} from 'react';
import {ViewStyle, TextStyle, StyleProp} from 'react-native';
import React, {useMemo} from 'react';
import {omit} from 'lodash';
import {
asBaseComponent,
forwardRef,
ForwardRefInjectedProps,
BaseComponentInjectedProps,
MarginModifiers,
PaddingModifiers,
TypographyModifiers,
ColorsModifiers
} from '../../commons/new';
import {asBaseComponent, forwardRef} from '../../commons/new';
import View from '../../components/view';
import {Colors} from '../../style';
import {useMeasure} from '../../hooks';
import {ValidationMessagePosition, Validator} from './types';
import {TextFieldProps, InternalTextFieldProps, ValidationMessagePosition, FieldContextType} from './types';
import {shouldHidePlaceholder} from './Presenter';
import Input, {InputProps} from './Input';
import ValidationMessage, {ValidationMessageProps} from './ValidationMessage';
import Label, {LabelProps} from './Label';
import FieldContext, {FieldContextType as _FieldContextType} from './FieldContext';
import useFieldState /* , FieldStateProps */ from './useFieldState';
import Input from './Input';
import ValidationMessage from './ValidationMessage';
import Label from './Label';
import FieldContext from './FieldContext';
import useFieldState from './useFieldState';
import usePreset from './usePreset';
import FloatingPlaceholder, {FloatingPlaceholderProps} from './FloatingPlaceholder';
import CharCounter, {CharCounterProps} from './CharCounter';

export type FieldContextType = _FieldContextType;
export type TextFieldProps = MarginModifiers &
PaddingModifiers &
TypographyModifiers &
ColorsModifiers &
InputProps &
LabelProps &
Omit<FloatingPlaceholderProps, 'testID'> &
// We're declaring these props explicitly here for react-docgen (which can't read hooks)
// FieldStateProps &
ValidationMessageProps &
Omit<CharCounterProps, 'maxLength' | 'testID'> & {
/**
* Pass to render a leading element
*/
leadingAccessory?: ReactElement;
/**
* Pass to render a trailing element
*/
trailingAccessory?: ReactElement;
/**
* Pass to render a bottom element below the input
*/
bottomAccessory?: ReactElement;
/**
* Pass to add floating placeholder support
*/
floatingPlaceholder?: boolean;
/**
* Custom style for the floating placeholder
*/
floatingPlaceholderStyle?: TextStyle;
/**
* A single or multiple validator. Can be a string (required, email) or custom function.
*/
validate?: Validator | Validator[];
/**
* Should validate when the TextField mounts
*/
validateOnStart?: boolean;
/**
* Should validate when the TextField value changes
*/
validateOnChange?: boolean;
/**
* Should validate when losing focus of TextField
*/
validateOnBlur?: boolean;
/**
* Callback for when field validity has changed
*/
onChangeValidity?: (isValid: boolean) => void;
/**
* The position of the validation message (top/bottom)
*/
validationMessagePosition?: ValidationMessagePosition;
/**
* Internal style for the field container
*/
fieldStyle?: StyleProp<ViewStyle>;
/**
* Internal dynamic style callback for the field container
*/
dynamicFieldStyle?: (context: FieldContextType, props: {preset: TextFieldProps['preset']}) => StyleProp<ViewStyle>;
/**
* Container style of the whole component
*/
containerStyle?: ViewStyle;
/**
* Predefined preset to use for styling the field
*/
preset?: 'default' | null | string;
};

export type InternalTextFieldProps = PropsWithChildren<
TextFieldProps &
// Omit<FieldStateInjectedProps, keyof InputProps> &
BaseComponentInjectedProps &
ForwardRefInjectedProps
>;
import FloatingPlaceholder from './FloatingPlaceholder';
import CharCounter from './CharCounter';

interface StaticMembers {
validationMessagePositions: typeof ValidationMessagePosition;
Expand Down Expand Up @@ -243,7 +152,13 @@ const TextField = (props: InternalTextFieldProps) => {
/>
)}
{bottomAccessory}
{showCharCounter && <CharCounter maxLength={others.maxLength} charCounterStyle={charCounterStyle} testID={`${props.testID}.charCounter`}/>}
{showCharCounter && (
<CharCounter
maxLength={others.maxLength}
charCounterStyle={charCounterStyle}
testID={`${props.testID}.charCounter`}
/>
)}
</View>
</View>
</FieldContext.Provider>
Expand All @@ -253,4 +168,5 @@ const TextField = (props: InternalTextFieldProps) => {
TextField.displayName = 'Incubator.TextField';
TextField.validationMessagePositions = ValidationMessagePosition;

export {TextFieldProps, FieldContextType};
export default asBaseComponent<TextFieldProps, StaticMembers>(forwardRef(TextField as any));
Loading