From 818a08f88e0469183956343b92c6a2f897c4b3cf Mon Sep 17 00:00:00 2001 From: Cameron McAteer Date: Tue, 19 Sep 2017 13:25:12 -0700 Subject: [PATCH 1/4] Add types for react-form@1.3 --- types/react-form/index.d.ts | 284 ++++++++++++++++++++++++++ types/react-form/react-form-tests.tsx | 78 +++++++ types/react-form/tsconfig.json | 24 +++ types/react-form/tslint.json | 1 + 4 files changed, 387 insertions(+) create mode 100644 types/react-form/index.d.ts create mode 100644 types/react-form/react-form-tests.tsx create mode 100644 types/react-form/tsconfig.json create mode 100644 types/react-form/tslint.json diff --git a/types/react-form/index.d.ts b/types/react-form/index.d.ts new file mode 100644 index 0000000000..43958a1935 --- /dev/null +++ b/types/react-form/index.d.ts @@ -0,0 +1,284 @@ +// Type definitions for react-form 1.3 +// Project: https://github.com/tannerlinsley/react-form#readme +// Definitions by: Cameron McAteer +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +declare module 'react-form' { + import * as React from 'react'; + + type FormValue = any; + type FormError = string | undefined; + type Nested = {[key: string]: T | Nested}; + export type FormValues = Nested; + export type Touched = Nested; + export type FormErrors = {[key: string]: FormError} | [{[key: string]: FormError}]; + export type NestedErrors = Nested; + 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): void; + resetForm(): void; + submitForm(e?: Pick, 'preventDefault'>): void; + } + + export interface FormFunctionProps extends FormProps, FormState, FormApi {} + + export class Form + extends React.Component< + FormProps & { children?: ((props: FormFunctionProps) => RenderReturn) | RenderReturn }, + FormState + > + implements FormApi, React.ChildContextProvider<{}> { + + static defaultProps: FormProps; + static childContextTypes: { + formApi: React.Validator + }; + + getDefaultState (): FormState; + getChildContext: () => { formApi: FormApi } + componentWillMount(): void; + componentWillReceiveProps(nextProps: Readonly>, 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): void; + resetForm (): void; + submitForm (e?: Pick, 'preventDefault'>): void; + + // Utils + getAPI (): FormApi; + setFormState (newState: Partial, silent?: boolean): void; + emitChange (state: FormState, initial?: boolean): void; + validate (values: FormValues, state: FormState, props: FormProps): FormErrors; + render (): RenderReturn; + } + + 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) => void; + resetForm: () => void; + submitForm: (e?: Pick, 'preventDefault'>) => void; + } + + interface FormFieldPropsWithField { + field?: string; + children: (api: FormFieldApi) => React.ReactElement | null; + } + interface FormFieldPropsWithoutField { + children: (api: FormApi) => RenderReturn; + } + export type FormFieldProps = FormFieldPropsWithField | FormFieldPropsWithoutField; + export const FormField: React.SFC; + + // FormError + export interface FormErrorProps { + field?: FormFieldPropsWithField['field']; + className?: string; + style?: React.HTMLAttributes['style']; + } + export const FormError: React.SFC; + + export interface FormInputProps { + field?: FormFieldPropsWithField['field']; + showErrors?: boolean; + errorBefore?: boolean; + isForm?: boolean; + className?: string; + errorProps?: {}; + } + + interface FormInputPropsWithChildren extends FormInputProps { + children: (api: FormFieldApi) => React.ReactElement | null; + } + export const FormInput: React.SFC; + + // ============================== + // Inputs + // ============================== + + export type EventHandler = (e: E, cb: () => void) => void; + export type ChangeHandler = EventHandler>; + export type FocusHandler = EventHandler>; + export type ClickHandler = EventHandler>; + + // Prop interfaces are intermediate interfaces to "redefine" the type of some events + // onChange:React.EventHandler => onChange:any => onChange:CustomEventHandler + + interface SelectOption { + label: string; + value: any; + disabled?: boolean; + } + interface SelectAttrs extends React.SelectHTMLAttributes { + onChange?: any; + onBlur?: any; + } + export interface SelectProps extends SelectAttrs { + options: ReadonlyArray + field?: FormInputProps['field']; + showErrors?: FormInputProps['showErrors']; + errorBefore?: FormInputProps['errorBefore']; + onChange?: ChangeHandler; + onBlur?: FocusHandler; + isForm?: FormInputProps['isForm']; + noTouch?: boolean; + errorProps?: FormInputProps['errorProps']; + placeholder?: string; + } + export const Select: React.SFC; + + interface InputAttrs extends React.InputHTMLAttributes { + onChange?: any; + onBlur?: any; + } + export interface CheckboxProps extends InputAttrs { + field?: FormInputProps['field']; + showErrors?: FormInputProps['showErrors']; + errorBefore?: FormInputProps['errorBefore']; + onChange?: ChangeHandler; + onBlur?: FocusHandler; + isForm?: FormInputProps['isForm']; + noTouch?: boolean; + errorProps?: FormInputProps['errorProps']; + } + export const Checkbox: React.SFC; + + interface TextareaAttrs extends React.TextareaHTMLAttributes { + onChange?: any; + onBlur?: any; + } + export interface TextareaProps extends TextareaAttrs { + field?: FormInputProps['field']; + showErrors?: FormInputProps['showErrors']; + errorBefore?: FormInputProps['errorBefore']; + onChange?: ChangeHandler + onBlur?: FocusHandler + isForm?: FormInputProps['isForm']; + noTouch?: boolean; + errorProps?: FormInputProps['errorProps']; + } + export const Textarea: React.SFC; + + export interface NestedFormProps extends FormProps { + field?: FormInputProps['field']; + children?: React.ReactElement | [React.ReactElement]; + errorProps?: FormInputProps['errorProps']; + } + export const NestedForm: React.SFC; + + export interface TextProps extends InputAttrs { + field?: FormInputProps['field']; + showErrors?: FormInputProps['showErrors']; + errorBefore?: FormInputProps['errorBefore']; + onChange?: ChangeHandler; + onBlur?: FocusHandler; + isForm?: FormInputProps['isForm']; + noTouch?: boolean; + errorProps?: FormInputProps['errorProps']; + } + export const Text: React.SFC; + + 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 implements FormFieldApi { + static childContextTypes: { + formRadioGroup: React.Validator + }; + + 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; + } + + interface InputWIthoutClick extends InputAttrs { + onClick?: any; + } + export interface RadioProps extends InputWIthoutClick { + onClick?: ClickHandler; + onChange?: ChangeHandler; + onBlur?: FocusHandler; + } + export class Radio extends React.Component { + + static contextTypes: { + formRadioGroup: React.Validator + }; + + context: RadioGroupContext; + } +} diff --git a/types/react-form/react-form-tests.tsx b/types/react-form/react-form-tests.tsx new file mode 100644 index 0000000000..aad7f1b92f --- /dev/null +++ b/types/react-form/react-form-tests.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import { + Form, + FormError, + FormInput, + + // Inputs + Select, + Checkbox, + Textarea, + NestedForm, + Text, + RadioGroup, + Radio +} from 'react-form'; + +
; + + + {() => null} +
; + +
+ {() =>
} +; + +
+ { ({ submitForm }) => } +
; + +; + +const CustomInput: React.SFC & {field?: string}> = + ({field, ...rest}) => { + return ( + + {({ setValue, getValue, setTouched }) => { + return ( + setValue(e.target.value)} + onBlur={() => setTouched(true)} + /> + ); + }} + + ); + }; + +const events = { + onChange: (e: React.SyntheticEvent, cb: () => void): null => null, + onBlur: (e: React.SyntheticEvent, cb: () => void): null => null +}; +const onClick = (e: React.SyntheticEvent, cb: () => void): null => null; + +; + +; +; + +