mirror of
https://github.com/zhigang1992/DefinitelyTyped.git
synced 2026-05-29 00:51:29 +08:00
Merge pull request #21981 from Arcath/react-form-2
Update react-form typings to support 2.12.1
This commit is contained in:
326
types/react-form/index.d.ts
vendored
326
types/react-form/index.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
// Type definitions for react-form 1.3
|
||||
// Type definitions for react-form 2.12
|
||||
// Project: https://github.com/tannerlinsley/react-form#readme
|
||||
// Definitions by: Cameron McAteer <https://github.com/cameron-mcateer>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
// Helper Types
|
||||
export type FormValue = any;
|
||||
export type FormError = string | undefined;
|
||||
export interface Nested<T> {
|
||||
@@ -13,24 +14,12 @@ export interface Nested<T> {
|
||||
}
|
||||
export type FormValues = Nested<FormValue>;
|
||||
export type Touched = Nested<boolean>;
|
||||
export type FormErrors = {[key: string]: FormError} | [{[key: string]: FormError}];
|
||||
export interface FormErrors {
|
||||
[key: string]: FormError;
|
||||
}
|
||||
export type NestedErrors = Nested<FormErrors>;
|
||||
export type RenderReturn = JSX.Element | false | null;
|
||||
|
||||
export interface FormProps {
|
||||
loadState?(props: FormProps, self: Form): FormState | undefined;
|
||||
defaultValues?: FormValues;
|
||||
preValidate?(values: FormValues, state: FormState, props: FormProps, self: Form): FormValues;
|
||||
validate?(values: FormValues, state: FormState, props: FormProps): FormErrors;
|
||||
onValidationFail?(values: FormValues, state: FormState, props: FormProps, self: Form): void;
|
||||
onChange?(state: FormState, props: FormProps, initial: boolean | FormProps, self: Form): void;
|
||||
saveState?(state: FormState, props: FormProps, self: Form): void;
|
||||
willUnmount?(state: FormState, props: FormProps, self: Form): void;
|
||||
preSubmit?(values: FormValues, state: FormState, props: FormProps, self: Form): FormValues;
|
||||
onSubmit?(values: FormValues, state: FormState, props: FormProps, self: Form): void;
|
||||
postSubmit?(values: FormValues, state: FormState, props: FormProps, self: Form): void;
|
||||
}
|
||||
|
||||
export interface FormState {
|
||||
values: FormValues;
|
||||
touched: Touched;
|
||||
@@ -39,24 +28,56 @@ export interface FormState {
|
||||
dirty?: boolean;
|
||||
}
|
||||
|
||||
export const FormDefaultProps: FormProps;
|
||||
export interface FormProps {
|
||||
dontValidateOnMount?: boolean;
|
||||
defaultValues?: FormValues;
|
||||
onSubmit?(values: FormValues, submissionEvent: React.SyntheticEvent<any>, formApi: FormApi): void;
|
||||
preSubmit?(values: FormValues, formApi: FormApi): FormValues;
|
||||
onSubmitFailure?(errors: FormErrors, formApi: FormApi): void;
|
||||
formDidUpdate?(formState: FormState): void;
|
||||
preValidate?(values: FormValues): FormValues;
|
||||
validateError?: ValidateValuesFunction;
|
||||
validateWarning?: ValidateValuesFunction;
|
||||
validateSuccess?: (values: FormValues, errors: FormErrors) => FormErrors;
|
||||
asyncValidators?: {
|
||||
[field: string]: (value: FormValue) => Promise<any>
|
||||
};
|
||||
dontPreventDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface FormApi {
|
||||
setAllValues(values: FormValues, noTouch?: boolean): void;
|
||||
setValue(field: string, value: any, noTouch?: boolean): void;
|
||||
getValue(field: string, fallback?: any): any;
|
||||
setNestedError(field: string, value?: boolean): void;
|
||||
getError(field: string): FormError;
|
||||
setTouched(field: string, value?: boolean): void;
|
||||
getTouched(field: string): boolean;
|
||||
addValue(field: string, value: any): void;
|
||||
removeValue(field: string, index: number): void;
|
||||
swapValues(field: string, index: number, destIndex: number): void;
|
||||
setAllTouched(dirty?: boolean, state?: Partial<FormState>): void;
|
||||
resetForm(): void;
|
||||
submitForm(e?: Pick<React.SyntheticEvent<any>, 'preventDefault'>): void;
|
||||
// State
|
||||
values: FormValues;
|
||||
touched: Touched;
|
||||
errors: FormErrors;
|
||||
warnings: FormErrors;
|
||||
successes: FormErrors;
|
||||
submits: number;
|
||||
submitted: boolean;
|
||||
asyncValidations: number;
|
||||
validating: {[field: string]: boolean};
|
||||
validationFailures: number;
|
||||
validationFailed: {[field: string]: boolean};
|
||||
|
||||
// Methods
|
||||
submitForm(event: React.SyntheticEvent<any>): void;
|
||||
setValue(fieldName: string, value: any): void;
|
||||
setAllValues(values: FormValues): void;
|
||||
setError(field: string, error: string): void;
|
||||
setWarning(field: string, warning: string): void;
|
||||
setSuccess(field: string, success: string): void;
|
||||
setTouched(field: string, touched: boolean): void;
|
||||
setAllTouched(touches: {[field: string]: boolean}): void;
|
||||
addValue(name: string, value: any): void;
|
||||
removeValue(name: string, index: number): void;
|
||||
swapValues(name: string, index1: number, index2: number): void;
|
||||
resetAll(): void;
|
||||
getFormState(): FormState;
|
||||
setFormState(state: FormState): void;
|
||||
}
|
||||
|
||||
export type ValidateValuesFunction = (values: FormValues) => FormErrors;
|
||||
|
||||
export interface FormFunctionProps extends FormProps, FormState, FormApi {}
|
||||
|
||||
export interface FormContext {
|
||||
@@ -65,10 +86,9 @@ export interface FormContext {
|
||||
|
||||
export class Form
|
||||
extends React.Component<
|
||||
FormProps & { children?: ((props: FormFunctionProps) => RenderReturn) | RenderReturn },
|
||||
FormState
|
||||
FormProps & { children?: ((props: FormFunctionProps) => RenderReturn) | RenderReturn }
|
||||
>
|
||||
implements FormApi, React.ChildContextProvider<FormContext> {
|
||||
implements React.ChildContextProvider<FormContext> {
|
||||
static defaultProps: FormProps;
|
||||
static childContextTypes: {
|
||||
formApi: React.Validator<any>
|
||||
@@ -80,207 +100,87 @@ export class Form
|
||||
componentWillReceiveProps(nextProps: Readonly<Partial<FormProps>>, nextContext: any): void;
|
||||
componentWillUmount(): void;
|
||||
|
||||
// API
|
||||
setAllValues(values: FormValues, noTouch?: boolean): void;
|
||||
setValue(field: string, value: any, noTouch?: boolean): void;
|
||||
getValue(field: string, fallback?: any): any;
|
||||
setNestedError(field: string, value?: boolean): void;
|
||||
getError(field: string): FormError;
|
||||
setTouched(field: string, value?: boolean): void;
|
||||
getTouched(field: string): boolean;
|
||||
addValue(field: string, value: any): void;
|
||||
removeValue(field: string, index: number): void;
|
||||
swapValues(field: string, index: number, destIndex: number): void;
|
||||
setAllTouched(dirty?: boolean, state?: Partial<FormState>): void;
|
||||
resetForm(): void;
|
||||
submitForm(e?: Pick<React.SyntheticEvent<any>, 'preventDefault'>): void;
|
||||
|
||||
// Utils
|
||||
getAPI(): FormApi;
|
||||
setFormState(newState: Partial<FormState>, silent?: boolean): void;
|
||||
emitChange(state: FormState, initial?: boolean): void;
|
||||
validate(values: FormValues, state: FormState, props: FormProps): FormErrors;
|
||||
render(): RenderReturn;
|
||||
}
|
||||
|
||||
export interface FormFieldApi {
|
||||
setAllValues(values: FormValues, noTouch?: boolean): void;
|
||||
setValue(value: any, noTouch?: boolean): void;
|
||||
getValue(fallback?: any): any;
|
||||
setNestedError(value?: boolean): void;
|
||||
export const NestedForm: React.StatelessComponent<FieldProps>;
|
||||
|
||||
export function FormField(component: React.ComponentType<any>): React.ComponentClass<any>;
|
||||
|
||||
// Fields
|
||||
|
||||
export interface FieldApi {
|
||||
getValue(): FormValue;
|
||||
getError(): FormError;
|
||||
setTouched(value?: boolean): void;
|
||||
getWarning(): FormError;
|
||||
getSuccess(): FormError;
|
||||
getTouched(): boolean;
|
||||
addValue(value: any): void;
|
||||
removeValue(index: number): void;
|
||||
swapValues(index: number, destIndex: number): void;
|
||||
setAllTouched(dirty?: boolean, state?: Partial<FormState>): void;
|
||||
resetForm(): void;
|
||||
submitForm(e?: Pick<React.SyntheticEvent<any>, 'preventDefault'>): void;
|
||||
getFieldName(): string;
|
||||
setValue(value: FormValue): void;
|
||||
setError(error: FormError): void;
|
||||
setWarning(warning: FormError): void;
|
||||
setSuccess(success: FormError): void;
|
||||
setTouched(touched: boolean): void;
|
||||
}
|
||||
|
||||
export interface FormFieldPropsWithField {
|
||||
field?: string;
|
||||
children(api: FormFieldApi): React.ReactElement<any> | null;
|
||||
}
|
||||
export interface FormFieldPropsWithoutField {
|
||||
children(api: FormApi): RenderReturn;
|
||||
}
|
||||
export type FormFieldProps = FormFieldPropsWithField | FormFieldPropsWithoutField;
|
||||
export const FormField: React.SFC<FormFieldProps>;
|
||||
|
||||
// FormError
|
||||
export interface FormErrorProps {
|
||||
field?: FormFieldPropsWithField['field'];
|
||||
className?: string;
|
||||
style?: React.HTMLAttributes<HTMLElement>['style'];
|
||||
}
|
||||
export const FormError: React.SFC<FormErrorProps>;
|
||||
|
||||
export interface FormInputProps {
|
||||
field?: FormFieldPropsWithField['field'];
|
||||
export interface FieldProps {
|
||||
field?: string | string[] | React.ReactText[] | Array<(string | React.ReactText[])>;
|
||||
showErrors?: boolean;
|
||||
errorBefore?: boolean;
|
||||
isForm?: boolean;
|
||||
className?: string;
|
||||
errorProps?: FormErrorProps;
|
||||
}
|
||||
|
||||
export interface FormInputPropsWithChildren extends FormInputProps {
|
||||
children(api: FormFieldApi): React.ReactElement<any> | null;
|
||||
}
|
||||
export const FormInput: React.SFC<FormInputPropsWithChildren>;
|
||||
export type SelectOptions = Array<{
|
||||
value: FormValue
|
||||
label: string
|
||||
}>;
|
||||
|
||||
// ==============================
|
||||
// Inputs
|
||||
// ==============================
|
||||
export interface SelectProps extends FieldProps, React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
options: SelectOptions;
|
||||
}
|
||||
|
||||
export type EventHandler<T, E> = (e: E, cb: () => void) => void;
|
||||
export type ChangeHandler<T> = EventHandler<T, React.ChangeEvent<T>>;
|
||||
export type FocusHandler<T> = EventHandler<T, React.FocusEvent<T>>;
|
||||
export type ClickHandler<T> = EventHandler<T, React.MouseEvent<T>>;
|
||||
export const Select: React.StatelessComponent<SelectProps>;
|
||||
|
||||
// Prop interfaces are intermediate interfaces to "redefine" the type of some events
|
||||
// onChange:React.EventHandler => onChange:any => onChange:CustomEventHandler
|
||||
export const Text: React.StatelessComponent<FieldProps & React.InputHTMLAttributes<HTMLInputElement>>;
|
||||
export const TextArea: React.StatelessComponent<FieldProps & React.TextareaHTMLAttributes<HTMLTextAreaElement>>;
|
||||
|
||||
export interface SelectOption {
|
||||
label: string;
|
||||
value: any;
|
||||
disabled?: boolean;
|
||||
}
|
||||
export interface SelectAttrs extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
onChange?: any;
|
||||
onBlur?: any;
|
||||
}
|
||||
export interface SelectProps extends SelectAttrs {
|
||||
options: ReadonlyArray<SelectOption>;
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLSelectElement>;
|
||||
onBlur?: FocusHandler<HTMLSelectElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
placeholder?: string;
|
||||
}
|
||||
export const Select: React.SFC<SelectProps>;
|
||||
|
||||
export interface InputAttrs extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
onChange?: any;
|
||||
onBlur?: any;
|
||||
}
|
||||
export interface CheckboxProps extends InputAttrs {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLInputElement>;
|
||||
onBlur?: FocusHandler<HTMLInputElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const Checkbox: React.SFC<CheckboxProps>;
|
||||
|
||||
export interface TextareaAttrs extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
onChange?: any;
|
||||
onBlur?: any;
|
||||
}
|
||||
export interface TextareaProps extends TextareaAttrs {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLTextAreaElement>;
|
||||
onBlur?: FocusHandler<HTMLTextAreaElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const Textarea: React.SFC<TextareaProps>;
|
||||
|
||||
export interface NestedFormProps extends FormProps {
|
||||
field?: FormInputProps['field'];
|
||||
children?: React.ReactElement<FormProps> | [React.ReactElement<FormProps>];
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const NestedForm: React.SFC<NestedFormProps>;
|
||||
|
||||
export interface TextProps extends InputAttrs {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLInputElement>;
|
||||
onBlur?: FocusHandler<HTMLInputElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const Text: React.SFC<TextProps>;
|
||||
|
||||
export interface RadioGroupProps {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
isForm?: FormInputProps['isForm'];
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export interface RadioGroupContext {
|
||||
formRadioGroup: RadioGroup;
|
||||
group: FieldApi;
|
||||
}
|
||||
export class RadioGroup extends React.Component<RadioGroupProps> implements FormFieldApi {
|
||||
static childContextTypes: {
|
||||
formRadioGroup: React.Validator<any>
|
||||
|
||||
export class RadioGroup
|
||||
extends React.Component<
|
||||
FieldProps & { children?: ((props: FieldApi) => RenderReturn) | RenderReturn }
|
||||
>
|
||||
implements React.ChildContextProvider<RadioGroupContext> {
|
||||
getChildContext(): {
|
||||
group: FieldApi;
|
||||
};
|
||||
|
||||
setAllValues: FormFieldApi['setAllValues'];
|
||||
setValue: FormFieldApi['setValue'];
|
||||
getValue: FormFieldApi['getValue'];
|
||||
setNestedError: FormFieldApi['setNestedError'];
|
||||
getError: FormFieldApi['getError'];
|
||||
setTouched: FormFieldApi['setTouched'];
|
||||
getTouched: FormFieldApi['getTouched'];
|
||||
addValue: FormFieldApi['addValue'];
|
||||
removeValue: FormFieldApi['removeValue'];
|
||||
swapValues: FormFieldApi['swapValues'];
|
||||
setAllTouched: FormFieldApi['setAllTouched'];
|
||||
resetForm: FormFieldApi['resetForm'];
|
||||
submitForm: FormFieldApi['submitForm'];
|
||||
|
||||
getChildContext(): RadioGroupContext;
|
||||
}
|
||||
|
||||
export interface InputWIthoutClick extends InputAttrs {
|
||||
onClick?: any;
|
||||
export const Radio: React.StatelessComponent<FieldProps & React.InputHTMLAttributes<HTMLInputElement> & {group: FieldApi}>;
|
||||
export const Checkbox: React.StatelessComponent<FieldProps & React.InputHTMLAttributes<HTMLInputElement>>;
|
||||
|
||||
// Styled Fields
|
||||
|
||||
export interface StyledProps extends FieldProps {
|
||||
noMessage?: boolean;
|
||||
messageBefore?: boolean;
|
||||
touchValidation?: boolean;
|
||||
}
|
||||
export interface RadioProps extends InputWIthoutClick {
|
||||
onClick?: ClickHandler<HTMLInputElement>;
|
||||
onChange?: ChangeHandler<HTMLInputElement>;
|
||||
onBlur?: FocusHandler<HTMLInputElement>;
|
||||
}
|
||||
export class Radio extends React.Component<RadioProps> {
|
||||
static contextTypes: {
|
||||
formRadioGroup: React.Validator<any>
|
||||
|
||||
export const StyledCheckbox: React.StatelessComponent<StyledProps & React.InputHTMLAttributes<HTMLInputElement> & {label: string}>;
|
||||
export const StyledTextArea: React.StatelessComponent<StyledProps & React.TextareaHTMLAttributes<HTMLTextAreaElement>>;
|
||||
export const StyledSelect: React.StatelessComponent<StyledProps & SelectProps & React.InputHTMLAttributes<HTMLSelectElement>>;
|
||||
export const StyledText: React.StatelessComponent<StyledProps & React.InputHTMLAttributes<HTMLInputElement>>;
|
||||
export const StyledRadio: React.StatelessComponent<StyledProps & React.InputHTMLAttributes<HTMLInputElement> & {group: FieldApi, label: string}>;
|
||||
|
||||
export class StyledRadioGroup
|
||||
extends React.Component<
|
||||
StyledProps & { children?: ((props: FieldApi) => RenderReturn) | RenderReturn }
|
||||
>
|
||||
implements React.ChildContextProvider<RadioGroupContext> {
|
||||
getChildContext(): {
|
||||
group: FieldApi
|
||||
};
|
||||
|
||||
context: RadioGroupContext;
|
||||
}
|
||||
|
||||
@@ -1,78 +1,672 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Form,
|
||||
FormError,
|
||||
FormInput,
|
||||
|
||||
// Inputs
|
||||
Select,
|
||||
Checkbox,
|
||||
Textarea,
|
||||
NestedForm,
|
||||
Text,
|
||||
RadioGroup,
|
||||
Radio
|
||||
Form,
|
||||
Text,
|
||||
TextArea,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
Checkbox,
|
||||
NestedForm,
|
||||
FormValues,
|
||||
FormErrors,
|
||||
StyledText,
|
||||
StyledRadioGroup,
|
||||
StyledRadio,
|
||||
StyledTextArea,
|
||||
StyledCheckbox,
|
||||
StyledSelect,
|
||||
FieldApi,
|
||||
FormField,
|
||||
FormApi
|
||||
} from 'react-form';
|
||||
|
||||
<Form />;
|
||||
// Basic Form Example
|
||||
const statusOptions = [
|
||||
{
|
||||
label: 'Single',
|
||||
value: 'single'
|
||||
},
|
||||
{
|
||||
label: 'In a Relationship',
|
||||
value: 'relationship'
|
||||
},
|
||||
{
|
||||
label: "It's Complicated",
|
||||
value: 'complicated'
|
||||
}
|
||||
];
|
||||
|
||||
<Form>
|
||||
{() => null}
|
||||
</Form>;
|
||||
class BasicForm extends React.Component {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
<Form>
|
||||
{() => <div/>}
|
||||
</Form>;
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form onSubmit={submittedValues => this.setState({ submittedValues })}>
|
||||
{ formApi => (
|
||||
<form onSubmit={formApi.submitForm} id="form2">
|
||||
<label htmlFor="firstName">First name</label>
|
||||
<Text field="firstName" id="firstName" />
|
||||
<label htmlFor="lastName">Last name</label>
|
||||
<Text field="lastName" id="lastName" />
|
||||
<RadioGroup field="gender">
|
||||
{ group => (
|
||||
<div>
|
||||
<label htmlFor="male" className="mr-2">Male</label>
|
||||
<Radio group={group} value="male" id="male" className="mr-3 d-inline-block" />
|
||||
<label htmlFor="female" className="mr-2">Female</label>
|
||||
<Radio group={group} value="female" id="female" className="d-inline-block" />
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup>
|
||||
<label htmlFor="bio">Bio</label>
|
||||
<TextArea field="bio" id="bio" />
|
||||
<label htmlFor="authorize" className="mr-2">Authorize</label>
|
||||
<Checkbox field="authorize" id="authorize" className="d-inline-block" />
|
||||
<label htmlFor="status" className="d-block">Relationship status</label>
|
||||
<Select field="status" id="status" options={statusOptions} />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<Form>
|
||||
{ ({ submitForm }) => <button onClick={submitForm}>Submit</button> }
|
||||
</Form>;
|
||||
// Form with Arrays
|
||||
class FormWithArrays extends React.Component {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
<FormError field="" />;
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
onSubmit={submittedValues => this.setState({ submittedValues })}>
|
||||
{ formApi => (
|
||||
<form onSubmit={formApi.submitForm} id="form3">
|
||||
<label htmlFor="firstName2">First name</label>
|
||||
<Text field="firstName" id="firstName2" />
|
||||
<label htmlFor="friend1">Friend1</label>
|
||||
<Text field={['friends', 0]} id="friend1" />
|
||||
<label htmlFor="friend2">Friend2</label>
|
||||
<Text field={['friends', 1]} id="friend2" />
|
||||
<label htmlFor="friend3">Friend3</label>
|
||||
<Text field={['friends', 2]} id="friend3" />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const CustomInput: React.SFC<React.HTMLAttributes<HTMLInputElement> & {field?: string}> =
|
||||
({field, ...rest}) => {
|
||||
return (
|
||||
<FormInput field={field}>
|
||||
{({ setValue, getValue, setTouched }) => {
|
||||
return (
|
||||
<input
|
||||
{...rest}
|
||||
value={getValue()}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onBlur={() => setTouched(true)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</FormInput>
|
||||
);
|
||||
// Field Syntax
|
||||
const Friend = ({ i }: {i: number}) => (
|
||||
<div>
|
||||
<h2>Friend</h2>
|
||||
<label htmlFor={`friend-first-${i}`}>First name</label>
|
||||
<Text field={['friends', i, 'firstName']} id={`friend-first-${i}`} />
|
||||
<label htmlFor={`friend-last-${i}`}>Last name</label>
|
||||
<Text field={[['friends', i], 'lastName']} id={`friend-last-${i}`} />
|
||||
<label htmlFor={`friend-street-${i}`}>Street</label>
|
||||
<Text field={['friends', i, 'address', 'street']} id={`friend-street-${i}`} />
|
||||
<label htmlFor={`friend-zip-${i}`}>Zipcode</label>
|
||||
<Text field={['friends', i, 'lastName.zip']} id={`friend-zip-${i}`} />
|
||||
</div>
|
||||
);
|
||||
|
||||
class FormWithSpecialFieldSyntax extends React.Component {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
onSubmit={submittedValues => this.setState({ submittedValues })}>
|
||||
{ formApi => (
|
||||
<form onSubmit={formApi.submitForm} id="syntax-form">
|
||||
<label htmlFor="nickname1">Nickname</label>
|
||||
<Text field={['nicknames', 0]} id="nickname1" />
|
||||
<label htmlFor="nickname2">Nickname</label>
|
||||
<Text field={['nicknames', 1]} id="nickname2" />
|
||||
<Friend i={0} />
|
||||
<Friend i={1} />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested Form
|
||||
const Questions = () => (
|
||||
<NestedForm field="questions">
|
||||
<Form>
|
||||
{ formApi => (
|
||||
<div>
|
||||
<label htmlFor="color">Whats your favorite color?</label>
|
||||
<Text field="color" id="color" />
|
||||
<label htmlFor="food">Whats your favorite food?</label>
|
||||
<Text field="food" id="food" />
|
||||
<label htmlFor="car">Whats type of car do you drive?</label>
|
||||
<Text field="car" id="car" />
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
</NestedForm>
|
||||
);
|
||||
|
||||
class NestedFormExample extends React.Component {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form onSubmit={submittedValues => this.setState({ submittedValues })}>
|
||||
{ formApi => (
|
||||
<form onSubmit={formApi.submitForm} id="form4">
|
||||
<label htmlFor="firstName3">First name</label>
|
||||
<Text field="firstName" id="firstName3" />
|
||||
<Questions />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic Forms
|
||||
class DynamicForm extends React.Component {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
onSubmit={submittedValues => this.setState({ submittedValues })}>
|
||||
{ formApi => (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => formApi.addValue('siblings', '')}
|
||||
type="button"
|
||||
className="mb-4 mr-4 btn btn-success">Add Sibling</button>
|
||||
<form onSubmit={formApi.submitForm} id="dynamic-form">
|
||||
<label htmlFor="dynamic-first">First name</label>
|
||||
<Text field="firstName" id="dynamic-first" />
|
||||
{ formApi.values.siblings && formApi.values.siblings.map((sibling: string, i: number) => (
|
||||
<div key={`sibling${i}`}>
|
||||
<label htmlFor={`sibling-name-${i}`}>Name</label>
|
||||
<Text field={['siblings', i]} id={`sibling-name-${i}`} />
|
||||
<button
|
||||
onClick={() => formApi.removeValue('siblings', i)}
|
||||
type="button"
|
||||
className="mb-4 btn btn-danger">Remove</button>
|
||||
</div>
|
||||
))}
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Array of Nested Forms
|
||||
const MyFriend = ({ i }: {i: number}) => (
|
||||
<NestedForm field={['friends', i]} key={`nested-friend-${i}`}>
|
||||
<Form>
|
||||
{ formApi => (
|
||||
<div>
|
||||
<h2>Friend</h2>
|
||||
<label htmlFor={`nested-friend-first-${i}`}>First name</label>
|
||||
<Text field="firstName" id={`nested-friend-first-${i}`} />
|
||||
<label htmlFor={`nested-friend-last-${i}`}>Last name</label>
|
||||
<Text field="lastName" id={`nested-friend-last-${i}`} />
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
</NestedForm>
|
||||
);
|
||||
|
||||
class FormWithArrayOfNestedForms extends React.Component {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
onSubmit={submittedValues => this.setState({ submittedValues })}>
|
||||
{ formApi => (
|
||||
<div>
|
||||
<form onSubmit={formApi.submitForm} id="form3">
|
||||
<MyFriend i={0} />
|
||||
<MyFriend i={1} />
|
||||
<MyFriend i={2} />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Styled Form
|
||||
class StyledForm extends React.Component {
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
errorValidator = (values: FormValues) => {
|
||||
const validateFirstName = (firstName: string) => {
|
||||
return !firstName ? 'First name is required.' : undefined;
|
||||
};
|
||||
const validateLastName = (lastName: string) => {
|
||||
return !lastName ? 'Last name is required.' : undefined;
|
||||
};
|
||||
const validateGender = (gender: string) => {
|
||||
return !gender ? 'Gender is required.' : undefined;
|
||||
};
|
||||
const validateBio = (bio: string) => {
|
||||
return !bio ? 'Bio is required.' : undefined;
|
||||
};
|
||||
const validateAuthorize = (authorize: boolean) => {
|
||||
return !authorize ? 'Please check authorize.' : undefined;
|
||||
};
|
||||
const validateStatus = (status: string) => {
|
||||
return !status ? 'Status is required.' : undefined;
|
||||
};
|
||||
return {
|
||||
firstName: validateFirstName(values.firstName),
|
||||
lastName: validateLastName(values.lastName),
|
||||
gender: validateGender(values.gender),
|
||||
bio: validateBio(values.bio),
|
||||
authorize: validateAuthorize(values.authorize),
|
||||
status: validateStatus(values.status)
|
||||
};
|
||||
}
|
||||
|
||||
const events = {
|
||||
onChange: (e: React.SyntheticEvent<HTMLElement>, cb: () => void): null => null,
|
||||
onBlur: (e: React.SyntheticEvent<HTMLElement>, cb: () => void): null => null
|
||||
warningValidator = (values: FormValues) => {
|
||||
const validateFirstName = (firstName: string) => {
|
||||
return firstName && firstName.length < 2 ? 'First name must be longer than 2 characters.' : undefined;
|
||||
};
|
||||
const validateLastName = (lastName: string) => {
|
||||
return lastName && lastName.length < 2 ? 'Last name must be longer than 2 characters.' : undefined;
|
||||
};
|
||||
const validateBio = (bio: string) => {
|
||||
return bio && bio.replace(/s+/g, ' ').trim().split(' ').length < 5 ? 'Bio should have more than 5 words.' : undefined;
|
||||
};
|
||||
return {
|
||||
firstName: validateFirstName(values.firstName),
|
||||
lastName: validateLastName(values.lastName),
|
||||
gender: undefined,
|
||||
bio: validateBio(values.bio),
|
||||
authorize: undefined,
|
||||
status: undefined
|
||||
};
|
||||
}
|
||||
|
||||
successValidator = (values: FormValues, errors: FormErrors) => {
|
||||
const validateFirstName = () => {
|
||||
return !errors['firstName'] ? 'Nice name!' : undefined;
|
||||
};
|
||||
const validateLastName = () => {
|
||||
return !errors['lastName'] ? 'Your last name is sick!' : undefined;
|
||||
};
|
||||
const validateGender = () => {
|
||||
return !errors['gender'] ? 'Thanks for entering your gender.' : undefined;
|
||||
};
|
||||
const validateBio = () => {
|
||||
return !errors['bio'] ? 'Cool Bio!' : undefined;
|
||||
};
|
||||
const validateAuthorize = () => {
|
||||
return !errors['authorize'] ? 'You are now authorized.' : undefined;
|
||||
};
|
||||
const validateStatus = () => {
|
||||
return !errors['status'] ? 'Thanks for entering your status.' : undefined;
|
||||
};
|
||||
return {
|
||||
firstName: validateFirstName(),
|
||||
lastName: validateLastName(),
|
||||
gender: validateGender(),
|
||||
bio: validateBio(),
|
||||
authorize: validateAuthorize(),
|
||||
status: validateStatus()
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Form
|
||||
validateError={this.errorValidator}
|
||||
validateWarning={this.warningValidator}
|
||||
validateSuccess={this.successValidator}
|
||||
onSubmit={submittedValues => this.setState({ submittedValues })}>
|
||||
{ formApi => (
|
||||
<form onSubmit={formApi.submitForm} id="form2">
|
||||
<label htmlFor="firstName">First name</label>
|
||||
<StyledText field="firstName" id="firstName" />
|
||||
<label htmlFor="lastName">Last name</label>
|
||||
<StyledText field="lastName" id="lastName" />
|
||||
<label>Choose Gender</label>
|
||||
<StyledRadioGroup field="gender">
|
||||
{ group => (
|
||||
<div>
|
||||
<StyledRadio group={group} value="male" id="male" label="Male" className="mr-3 d-inline-block" />
|
||||
<StyledRadio group={group} value="female" id="female" label="Female" className="d-inline-block" />
|
||||
</div>
|
||||
)}
|
||||
</StyledRadioGroup>
|
||||
<label htmlFor="bio">Bio</label>
|
||||
<StyledTextArea field="bio" id="bio" />
|
||||
<StyledCheckbox field="authorize" id="authorize" label="Authorize" className="d-inline-block" />
|
||||
<label htmlFor="status" className="d-block">Relationship status</label>
|
||||
<StyledSelect field="status" id="status" options={statusOptions} />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
)}
|
||||
</Form>;
|
||||
}
|
||||
}
|
||||
|
||||
// Define a custom message component
|
||||
const Message = ({ color, message }: {color: string, message: string}) => {
|
||||
return (
|
||||
<div className="mb-4" style={{ color }}>
|
||||
<small>{message}</small>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const onClick = (e: React.SyntheticEvent<HTMLElement>, cb: () => void): null => null;
|
||||
|
||||
<Select options={[]} />;
|
||||
<Select field="" options={[{label: '', value: '', disabled: false}]} {...events} />;
|
||||
// Define your custom input
|
||||
// Note, the ...rest is important because it allows you to pass any
|
||||
// additional fields to the internal <input>.
|
||||
class CustomTextWrapper extends React.Component<{
|
||||
fieldApi: FieldApi
|
||||
onInput: any
|
||||
}> {
|
||||
render() {
|
||||
const {
|
||||
fieldApi,
|
||||
onInput,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
<Checkbox />;
|
||||
<Checkbox field="" checked={false} {...events} />;
|
||||
const {
|
||||
getValue,
|
||||
getError,
|
||||
getWarning,
|
||||
getSuccess,
|
||||
setValue,
|
||||
setTouched,
|
||||
} = fieldApi;
|
||||
|
||||
<Textarea />;
|
||||
<Textarea field="" {...events} />;
|
||||
const error = getError();
|
||||
const warning = getWarning();
|
||||
const success = getSuccess();
|
||||
|
||||
<Form>
|
||||
<NestedForm>
|
||||
<Form />
|
||||
</NestedForm>
|
||||
</Form>;
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={getValue()}
|
||||
onInput={(e) => {
|
||||
setValue(e.currentTarget.value);
|
||||
if (onInput) {
|
||||
onInput(e);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
setTouched(true);
|
||||
}}
|
||||
{...rest} />
|
||||
{ error ? <Message color="red" message={error} /> : null }
|
||||
{ !error && warning ? <Message color="orange" message={warning} /> : null }
|
||||
{ !error && !warning && success ? <Message color="green" message={success} /> : null }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<Text />;
|
||||
<Text field="" {...events} />;
|
||||
// Use the form field and your custom input together to create your very own input!
|
||||
const CustomText = FormField(CustomTextWrapper);
|
||||
|
||||
<RadioGroup field="">
|
||||
<Radio />
|
||||
<Radio {...events} onClick={onClick} />
|
||||
</RadioGroup>;
|
||||
const errorValidator = (values: FormValues) => {
|
||||
return {
|
||||
hello: !values.hello || !values.hello.match(/Hello World/) ? "Input must contain 'Hello World'" : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const warningValidator = (values: FormValues) => {
|
||||
return {
|
||||
hello: !values.hello ||
|
||||
!values.hello.match(/^Hello World$/) ? "Input should equal 'Hello World'" : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const successValidator = (values: FormValues) => {
|
||||
return {
|
||||
hello: values.hello && values.hello.match(/Hello World/) ? "Thanks for entering 'Hello World'!" : undefined
|
||||
};
|
||||
};
|
||||
|
||||
class FormWithCustomInput extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
validateWarning={warningValidator}
|
||||
validateSuccess={successValidator}
|
||||
validateError={errorValidator}>
|
||||
{ formApi => (
|
||||
<form onSubmit={formApi.submitForm} id="form5">
|
||||
<label htmlFor="firstName4">First name</label>
|
||||
<Text field="firstName" id="firstName4" />
|
||||
<label htmlFor="hello2">Custom hello world</label>
|
||||
<CustomText field="hello" id="hello2" />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Async Validators
|
||||
const aserrorValidator = (values: FormValues) => {
|
||||
return {
|
||||
username: !values.username || values.username.trim() === '' ? 'Username is a required field' : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const assuccessValidator = (values: FormValues, errors: FormErrors) => {
|
||||
return {
|
||||
username: !errors.username ? 'Awesome! your username is good to go!' : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const doesUsernameExist = (username: string) => new Promise((resolve, reject) => setTimeout(() => {
|
||||
// Simulate username check
|
||||
if (['joe', 'tanner', 'billy', 'bob'].indexOf(username)) {
|
||||
resolve({ error: 'That username is taken', success: null });
|
||||
}
|
||||
// Simulate request faulure
|
||||
if (username === 'reject') {
|
||||
reject('Failure while making call to validate username does not exist');
|
||||
}
|
||||
// Sumulate username success check
|
||||
resolve({});
|
||||
}, 2000));
|
||||
|
||||
const asyncValidators = {
|
||||
username: async (username: string) => {
|
||||
const validations = await doesUsernameExist(username);
|
||||
return validations;
|
||||
}
|
||||
};
|
||||
|
||||
class AsynchronousFormValidation extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
validateError={aserrorValidator}
|
||||
validateSuccess={assuccessValidator}
|
||||
asyncValidators={asyncValidators}>
|
||||
{ formApi => (
|
||||
<form onSubmit={formApi.submitForm} id="form6">
|
||||
<label htmlFor="username">Username</label>
|
||||
<Text field="username" id="username" />
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested Async Validators
|
||||
const NestedNestedFormContent = ({ formApi }: {formApi: FormApi}) => {
|
||||
return (
|
||||
<div>
|
||||
<label htmlFor="username4">Nested Username</label>
|
||||
<Text field="username" id="username4" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const NestedFormContent = ({ formApi }: {formApi: FormApi}) => {
|
||||
return (
|
||||
<div>
|
||||
<label htmlFor="username3">Nested Username</label>
|
||||
<Text field="username" id="username3" />
|
||||
<NestedForm field="nestednested">
|
||||
<Form
|
||||
validateError={errorValidator}
|
||||
validateSuccess={successValidator}
|
||||
asyncValidators={asyncValidators3}>
|
||||
{
|
||||
formApi =>
|
||||
<NestedNestedFormContent formApi={formApi} />
|
||||
}
|
||||
</Form>
|
||||
</NestedForm>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FormContent = ({ formApi }: {formApi: FormApi}) => {
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={formApi.submitForm} id="form7">
|
||||
<label htmlFor="username2">Username</label>
|
||||
<Text field="username" id="username2" />
|
||||
<NestedForm field="nested">
|
||||
<Form
|
||||
validateError={errorValidator}
|
||||
validateSuccess={successValidator}
|
||||
asyncValidators={asyncValidators2}>
|
||||
{
|
||||
formApi =>
|
||||
<NestedFormContent formApi={formApi} />
|
||||
}
|
||||
</Form>
|
||||
</NestedForm>
|
||||
<button type="submit" className="mb-4 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const naserrorValidator = (values: FormValues) => {
|
||||
return {
|
||||
username: !values.username || values.username.trim() === '' ? 'Username is a required field' : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const nassuccessValidator = (values: FormValues, errors: FormErrors) => {
|
||||
return {
|
||||
username: !errors.username ? 'Awesome! your username is good to go!' : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const nasdoesUsernameExist = (username: string, ms: number) => new Promise((resolve, reject) => setTimeout(() => {
|
||||
// Simulate username check
|
||||
if (['joe', 'tanner', 'billy', 'bob'].indexOf(username)) {
|
||||
resolve({ error: 'That username is taken', success: null });
|
||||
}
|
||||
// Simulate request faulure
|
||||
if (username === 'reject') {
|
||||
reject('Failure while making call to validate username does not exist');
|
||||
}
|
||||
// Sumulate username success check
|
||||
resolve({});
|
||||
}, ms));
|
||||
|
||||
const nasasyncValidators = {
|
||||
username: async (username: string) => {
|
||||
const validations = await nasdoesUsernameExist(username, 2000);
|
||||
return validations;
|
||||
}
|
||||
};
|
||||
|
||||
const asyncValidators2 = {
|
||||
username: async (username: string) => {
|
||||
const validations = await nasdoesUsernameExist(username, 4000);
|
||||
return validations;
|
||||
}
|
||||
};
|
||||
|
||||
const asyncValidators3 = {
|
||||
username: async (username: string) => {
|
||||
const validations = await nasdoesUsernameExist(username, 6000);
|
||||
return validations;
|
||||
}
|
||||
};
|
||||
|
||||
class NestedAsynchronousFormValidation extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
validateError={naserrorValidator}
|
||||
validateSuccess={nassuccessValidator}
|
||||
asyncValidators={nasasyncValidators}>
|
||||
{
|
||||
formApi =>
|
||||
<FormContent formApi={formApi} />
|
||||
}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
286
types/react-form/v1/index.d.ts
vendored
Normal file
286
types/react-form/v1/index.d.ts
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
// Type definitions for react-form 1.3
|
||||
// Project: https://github.com/tannerlinsley/react-form#readme
|
||||
// Definitions by: Cameron McAteer <https://github.com/cameron-mcateer>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.3
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
export type FormValue = any;
|
||||
export type FormError = string | undefined;
|
||||
export interface Nested<T> {
|
||||
[key: string]: T | Nested<T>;
|
||||
}
|
||||
export type FormValues = Nested<FormValue>;
|
||||
export type Touched = Nested<boolean>;
|
||||
export type FormErrors = {[key: string]: FormError} | [{[key: string]: FormError}];
|
||||
export type NestedErrors = Nested<FormErrors>;
|
||||
export type RenderReturn = JSX.Element | false | null;
|
||||
|
||||
export interface FormProps {
|
||||
loadState?(props: FormProps, self: Form): FormState | undefined;
|
||||
defaultValues?: FormValues;
|
||||
preValidate?(values: FormValues, state: FormState, props: FormProps, self: Form): FormValues;
|
||||
validate?(values: FormValues, state: FormState, props: FormProps): FormErrors;
|
||||
onValidationFail?(values: FormValues, state: FormState, props: FormProps, self: Form): void;
|
||||
onChange?(state: FormState, props: FormProps, initial: boolean | FormProps, self: Form): void;
|
||||
saveState?(state: FormState, props: FormProps, self: Form): void;
|
||||
willUnmount?(state: FormState, props: FormProps, self: Form): void;
|
||||
preSubmit?(values: FormValues, state: FormState, props: FormProps, self: Form): FormValues;
|
||||
onSubmit?(values: FormValues, state: FormState, props: FormProps, self: Form): void;
|
||||
postSubmit?(values: FormValues, state: FormState, props: FormProps, self: Form): void;
|
||||
}
|
||||
|
||||
export interface FormState {
|
||||
values: FormValues;
|
||||
touched: Touched;
|
||||
errors: FormErrors;
|
||||
nestedErrors: NestedErrors;
|
||||
dirty?: boolean;
|
||||
}
|
||||
|
||||
export const FormDefaultProps: FormProps;
|
||||
|
||||
export interface FormApi {
|
||||
setAllValues(values: FormValues, noTouch?: boolean): void;
|
||||
setValue(field: string, value: any, noTouch?: boolean): void;
|
||||
getValue(field: string, fallback?: any): any;
|
||||
setNestedError(field: string, value?: boolean): void;
|
||||
getError(field: string): FormError;
|
||||
setTouched(field: string, value?: boolean): void;
|
||||
getTouched(field: string): boolean;
|
||||
addValue(field: string, value: any): void;
|
||||
removeValue(field: string, index: number): void;
|
||||
swapValues(field: string, index: number, destIndex: number): void;
|
||||
setAllTouched(dirty?: boolean, state?: Partial<FormState>): void;
|
||||
resetForm(): void;
|
||||
submitForm(e?: Pick<React.SyntheticEvent<any>, 'preventDefault'>): void;
|
||||
}
|
||||
|
||||
export interface FormFunctionProps extends FormProps, FormState, FormApi {}
|
||||
|
||||
export interface FormContext {
|
||||
formApi: FormApi;
|
||||
}
|
||||
|
||||
export class Form
|
||||
extends React.Component<
|
||||
FormProps & { children?: ((props: FormFunctionProps) => RenderReturn) | RenderReturn },
|
||||
FormState
|
||||
>
|
||||
implements FormApi, React.ChildContextProvider<FormContext> {
|
||||
static defaultProps: FormProps;
|
||||
static childContextTypes: {
|
||||
formApi: React.Validator<any>
|
||||
};
|
||||
|
||||
getDefaultState(): FormState;
|
||||
getChildContext(): FormContext;
|
||||
componentWillMount(): void;
|
||||
componentWillReceiveProps(nextProps: Readonly<Partial<FormProps>>, nextContext: any): void;
|
||||
componentWillUmount(): void;
|
||||
|
||||
// API
|
||||
setAllValues(values: FormValues, noTouch?: boolean): void;
|
||||
setValue(field: string, value: any, noTouch?: boolean): void;
|
||||
getValue(field: string, fallback?: any): any;
|
||||
setNestedError(field: string, value?: boolean): void;
|
||||
getError(field: string): FormError;
|
||||
setTouched(field: string, value?: boolean): void;
|
||||
getTouched(field: string): boolean;
|
||||
addValue(field: string, value: any): void;
|
||||
removeValue(field: string, index: number): void;
|
||||
swapValues(field: string, index: number, destIndex: number): void;
|
||||
setAllTouched(dirty?: boolean, state?: Partial<FormState>): void;
|
||||
resetForm(): void;
|
||||
submitForm(e?: Pick<React.SyntheticEvent<any>, 'preventDefault'>): void;
|
||||
|
||||
// Utils
|
||||
getAPI(): FormApi;
|
||||
setFormState(newState: Partial<FormState>, silent?: boolean): void;
|
||||
emitChange(state: FormState, initial?: boolean): void;
|
||||
validate(values: FormValues, state: FormState, props: FormProps): FormErrors;
|
||||
render(): RenderReturn;
|
||||
}
|
||||
|
||||
export interface FormFieldApi {
|
||||
setAllValues(values: FormValues, noTouch?: boolean): void;
|
||||
setValue(value: any, noTouch?: boolean): void;
|
||||
getValue(fallback?: any): any;
|
||||
setNestedError(value?: boolean): void;
|
||||
getError(): FormError;
|
||||
setTouched(value?: boolean): void;
|
||||
getTouched(): boolean;
|
||||
addValue(value: any): void;
|
||||
removeValue(index: number): void;
|
||||
swapValues(index: number, destIndex: number): void;
|
||||
setAllTouched(dirty?: boolean, state?: Partial<FormState>): void;
|
||||
resetForm(): void;
|
||||
submitForm(e?: Pick<React.SyntheticEvent<any>, 'preventDefault'>): void;
|
||||
}
|
||||
|
||||
export interface FormFieldPropsWithField {
|
||||
field?: string;
|
||||
children(api: FormFieldApi): React.ReactElement<any> | null;
|
||||
}
|
||||
export interface FormFieldPropsWithoutField {
|
||||
children(api: FormApi): RenderReturn;
|
||||
}
|
||||
export type FormFieldProps = FormFieldPropsWithField | FormFieldPropsWithoutField;
|
||||
export const FormField: React.SFC<FormFieldProps>;
|
||||
|
||||
// FormError
|
||||
export interface FormErrorProps {
|
||||
field?: FormFieldPropsWithField['field'];
|
||||
className?: string;
|
||||
style?: React.HTMLAttributes<HTMLElement>['style'];
|
||||
}
|
||||
export const FormError: React.SFC<FormErrorProps>;
|
||||
|
||||
export interface FormInputProps {
|
||||
field?: FormFieldPropsWithField['field'];
|
||||
showErrors?: boolean;
|
||||
errorBefore?: boolean;
|
||||
isForm?: boolean;
|
||||
className?: string;
|
||||
errorProps?: FormErrorProps;
|
||||
}
|
||||
|
||||
export interface FormInputPropsWithChildren extends FormInputProps {
|
||||
children(api: FormFieldApi): React.ReactElement<any> | null;
|
||||
}
|
||||
export const FormInput: React.SFC<FormInputPropsWithChildren>;
|
||||
|
||||
// ==============================
|
||||
// Inputs
|
||||
// ==============================
|
||||
|
||||
export type EventHandler<T, E> = (e: E, cb: () => void) => void;
|
||||
export type ChangeHandler<T> = EventHandler<T, React.ChangeEvent<T>>;
|
||||
export type FocusHandler<T> = EventHandler<T, React.FocusEvent<T>>;
|
||||
export type ClickHandler<T> = EventHandler<T, React.MouseEvent<T>>;
|
||||
|
||||
// Prop interfaces are intermediate interfaces to "redefine" the type of some events
|
||||
// onChange:React.EventHandler => onChange:any => onChange:CustomEventHandler
|
||||
|
||||
export interface SelectOption {
|
||||
label: string;
|
||||
value: any;
|
||||
disabled?: boolean;
|
||||
}
|
||||
export interface SelectAttrs extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
onChange?: any;
|
||||
onBlur?: any;
|
||||
}
|
||||
export interface SelectProps extends SelectAttrs {
|
||||
options: ReadonlyArray<SelectOption>;
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLSelectElement>;
|
||||
onBlur?: FocusHandler<HTMLSelectElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
placeholder?: string;
|
||||
}
|
||||
export const Select: React.SFC<SelectProps>;
|
||||
|
||||
export interface InputAttrs extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
onChange?: any;
|
||||
onBlur?: any;
|
||||
}
|
||||
export interface CheckboxProps extends InputAttrs {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLInputElement>;
|
||||
onBlur?: FocusHandler<HTMLInputElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const Checkbox: React.SFC<CheckboxProps>;
|
||||
|
||||
export interface TextareaAttrs extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
onChange?: any;
|
||||
onBlur?: any;
|
||||
}
|
||||
export interface TextareaProps extends TextareaAttrs {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLTextAreaElement>;
|
||||
onBlur?: FocusHandler<HTMLTextAreaElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const Textarea: React.SFC<TextareaProps>;
|
||||
|
||||
export interface NestedFormProps extends FormProps {
|
||||
field?: FormInputProps['field'];
|
||||
children?: React.ReactElement<FormProps> | [React.ReactElement<FormProps>];
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const NestedForm: React.SFC<NestedFormProps>;
|
||||
|
||||
export interface TextProps extends InputAttrs {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
onChange?: ChangeHandler<HTMLInputElement>;
|
||||
onBlur?: FocusHandler<HTMLInputElement>;
|
||||
isForm?: FormInputProps['isForm'];
|
||||
noTouch?: boolean;
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export const Text: React.SFC<TextProps>;
|
||||
|
||||
export interface RadioGroupProps {
|
||||
field?: FormInputProps['field'];
|
||||
showErrors?: FormInputProps['showErrors'];
|
||||
errorBefore?: FormInputProps['errorBefore'];
|
||||
isForm?: FormInputProps['isForm'];
|
||||
errorProps?: FormInputProps['errorProps'];
|
||||
}
|
||||
export interface RadioGroupContext {
|
||||
formRadioGroup: RadioGroup;
|
||||
}
|
||||
export class RadioGroup extends React.Component<RadioGroupProps> implements FormFieldApi {
|
||||
static childContextTypes: {
|
||||
formRadioGroup: React.Validator<any>
|
||||
};
|
||||
|
||||
setAllValues: FormFieldApi['setAllValues'];
|
||||
setValue: FormFieldApi['setValue'];
|
||||
getValue: FormFieldApi['getValue'];
|
||||
setNestedError: FormFieldApi['setNestedError'];
|
||||
getError: FormFieldApi['getError'];
|
||||
setTouched: FormFieldApi['setTouched'];
|
||||
getTouched: FormFieldApi['getTouched'];
|
||||
addValue: FormFieldApi['addValue'];
|
||||
removeValue: FormFieldApi['removeValue'];
|
||||
swapValues: FormFieldApi['swapValues'];
|
||||
setAllTouched: FormFieldApi['setAllTouched'];
|
||||
resetForm: FormFieldApi['resetForm'];
|
||||
submitForm: FormFieldApi['submitForm'];
|
||||
|
||||
getChildContext(): RadioGroupContext;
|
||||
}
|
||||
|
||||
export interface InputWIthoutClick extends InputAttrs {
|
||||
onClick?: any;
|
||||
}
|
||||
export interface RadioProps extends InputWIthoutClick {
|
||||
onClick?: ClickHandler<HTMLInputElement>;
|
||||
onChange?: ChangeHandler<HTMLInputElement>;
|
||||
onBlur?: FocusHandler<HTMLInputElement>;
|
||||
}
|
||||
export class Radio extends React.Component<RadioProps> {
|
||||
static contextTypes: {
|
||||
formRadioGroup: React.Validator<any>
|
||||
};
|
||||
|
||||
context: RadioGroupContext;
|
||||
}
|
||||
78
types/react-form/v1/react-form-tests.tsx
Normal file
78
types/react-form/v1/react-form-tests.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Form,
|
||||
FormError,
|
||||
FormInput,
|
||||
|
||||
// Inputs
|
||||
Select,
|
||||
Checkbox,
|
||||
Textarea,
|
||||
NestedForm,
|
||||
Text,
|
||||
RadioGroup,
|
||||
Radio
|
||||
} from 'react-form';
|
||||
|
||||
<Form />;
|
||||
|
||||
<Form>
|
||||
{() => null}
|
||||
</Form>;
|
||||
|
||||
<Form>
|
||||
{() => <div/>}
|
||||
</Form>;
|
||||
|
||||
<Form>
|
||||
{ ({ submitForm }) => <button onClick={submitForm}>Submit</button> }
|
||||
</Form>;
|
||||
|
||||
<FormError field="" />;
|
||||
|
||||
const CustomInput: React.SFC<React.HTMLAttributes<HTMLInputElement> & {field?: string}> =
|
||||
({field, ...rest}) => {
|
||||
return (
|
||||
<FormInput field={field}>
|
||||
{({ setValue, getValue, setTouched }) => {
|
||||
return (
|
||||
<input
|
||||
{...rest}
|
||||
value={getValue()}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onBlur={() => setTouched(true)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</FormInput>
|
||||
);
|
||||
};
|
||||
|
||||
const events = {
|
||||
onChange: (e: React.SyntheticEvent<HTMLElement>, cb: () => void): null => null,
|
||||
onBlur: (e: React.SyntheticEvent<HTMLElement>, cb: () => void): null => null
|
||||
};
|
||||
const onClick = (e: React.SyntheticEvent<HTMLElement>, cb: () => void): null => null;
|
||||
|
||||
<Select options={[]} />;
|
||||
<Select field="" options={[{label: '', value: '', disabled: false}]} {...events} />;
|
||||
|
||||
<Checkbox />;
|
||||
<Checkbox field="" checked={false} {...events} />;
|
||||
|
||||
<Textarea />;
|
||||
<Textarea field="" {...events} />;
|
||||
|
||||
<Form>
|
||||
<NestedForm>
|
||||
<Form />
|
||||
</NestedForm>
|
||||
</Form>;
|
||||
|
||||
<Text />;
|
||||
<Text field="" {...events} />;
|
||||
|
||||
<RadioGroup field="">
|
||||
<Radio />
|
||||
<Radio {...events} onClick={onClick} />
|
||||
</RadioGroup>;
|
||||
30
types/react-form/v1/tsconfig.json
Normal file
30
types/react-form/v1/tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom"
|
||||
],
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"baseUrl": "../../",
|
||||
"jsx": "react",
|
||||
"typeRoots": [
|
||||
"../../"
|
||||
],
|
||||
"types": [],
|
||||
"noEmit": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"paths": {
|
||||
"react-form": [
|
||||
"react-form/v1"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"index.d.ts",
|
||||
"react-form-tests.tsx"
|
||||
]
|
||||
}
|
||||
1
types/react-form/v1/tslint.json
Normal file
1
types/react-form/v1/tslint.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "extends": "dtslint/dt.json" }
|
||||
Reference in New Issue
Block a user