From a4064398e3ce04ffacae73fb63642db513ffc395 Mon Sep 17 00:00:00 2001 From: ell10t Date: Tue, 30 Oct 2018 09:57:10 +0000 Subject: [PATCH 01/14] Started TypeScript definition file --- index.d.ts | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 index.d.ts diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 00000000..5e61989b --- /dev/null +++ b/index.d.ts @@ -0,0 +1,159 @@ +import * as React from 'react' + +export interface FormProviderProps { + rules?: ValidationSchema + messages?: ValidationMessages + debounceTime?: number +} + +export class FormProvider extends React.Component {} + +export interface Field { + Group: React.ComponentClass<{ name: string }> +} + +export interface InitialValues { + [key: string]: string | InitialValues +} + +export interface SerializedForm { + [key: string]: string | SerializedForm +} + +export interface FormErrors { + [key: string]: string | string[] | FormErrors +} + +export interface Fields { + [key: string]: Field | Fields +} + +export interface ValidationSchema {} + +export interface ValidationMessages { + [key: string]: string | string[] | ValidationMessages +} + +type GenericFormEvent = (args: { fields: Fields; form: Form }) => void +type SubmitFormEvent = ( + args: { serialized: SerializedForm; fields: Fields; form: Form }, +) => void + +export interface FormProps { + innerRef?: () => void + action: () => void | Promise + initialValues?: InitialValues + + /* Validation */ + rules?: ValidationSchema + messages?: ValidationMessages + + /* Callbacks */ + onFirstChange?: ( + args: { + event: React.FormEvent + nextValue: string + prevValue: string + fieldProps: object + fields: Fields + form: Form + }, + ) => void + onClear?: GenericFormEvent + onReset?: GenericFormEvent + onSerialize?: (args: { serialized: SerializedForm }) => void + onInvalid?: ( + args: { invalidFields: Fields; fields: Fields; form: Form }, + ) => void + onSubmitStart?: SubmitFormEvent + onSubmitted?: SubmitFormEvent + onSubmitFailed?: SubmitFormEvent + onSubmitEnd?: SubmitFormEvent +} + +export class Form extends React.Component { + reset: () => void + serialize: () => SerializedForm + setErrors: FormErrors + submit: () => Promise + validate: () => Promise +} + +export interface RuleParameters { + get: (path: string[]) => string | undefined + value: any + fieldProps: any + fields: Fields + form: Form +} + +export type AsyncRulePayload = { + valid: boolean + extra?: { + [exraKey: string]: any + } +} + +interface Event { + event: React.FormEvent + fieldProps: any + fields: Fields + form: Form +} + +export interface BlurEvent extends Event {} +export interface FocusEvent extends Event {} + +export interface ChangeEvent extends Event { + event: React.FormEvent + prevValue: any + nextValue: any +} + +export interface FieldProps { + rule?: RegExp | ((args: RuleParameters) => boolean) + asyncRule?: RegExp | ((args: RuleParameters) => AsyncRulePayload) + required?: boolean + skip?: boolean + onBlur?: (args: BlurEvent) => void + onChange?: (args: ChangeEvent) => void + onFocus?: (args: FocusEvent) => void +} + +export interface CreateFieldState { + required: boolean + validating: boolean + validatedSync: boolean + validatedAsync: boolean + valid: boolean + validSync: boolean + validAsync: boolean + invalid: boolean + errors?: string[] +} + +export interface CreateFieldProps { + fieldProps: any + fieldState: CreateFieldState +} + +type FieldPreset = + | React.InputHTMLAttributes + | React.InputHTMLAttributes + | React.TextareaHTMLAttributes + +export const fieldPresets: { + checkbox: React.InputHTMLAttributes + input: React.InputHTMLAttributes + radio: React.InputHTMLAttributes + select: React.InputHTMLAttributes + textarea: React.TextareaHTMLAttributes +} + +export function createField

( + preset: FieldPreset, +): ( + component: ( + props: CreateFieldProps & P, + ) => React.ReactElement, +) => React.ComponentClass diff --git a/package.json b/package.json index 6b14deb5..fadb7e97 100644 --- a/package.json +++ b/package.json @@ -116,5 +116,6 @@ "invariant": "^2.2.4", "ramda": "^0.25.0", "rxjs": "^6.3.2" - } + }, + "typings": "index.d.ts" } From 5367187d129c6d12acaa59165bad1c58e96f9063 Mon Sep 17 00:00:00 2001 From: ell10t Date: Tue, 30 Oct 2018 11:27:50 +0000 Subject: [PATCH 02/14] Adds missing field state values --- index.d.ts | 59 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/index.d.ts b/index.d.ts index 5e61989b..c63156bf 100644 --- a/index.d.ts +++ b/index.d.ts @@ -35,6 +35,7 @@ export interface ValidationMessages { } type GenericFormEvent = (args: { fields: Fields; form: Form }) => void + type SubmitFormEvent = ( args: { serialized: SerializedForm; fields: Fields; form: Form }, ) => void @@ -110,31 +111,59 @@ export interface ChangeEvent extends Event { nextValue: any } +export type Rule = RegExp | ((args: RuleParameters) => boolean) +export type AsyncRule = RegExp | ((args: RuleParameters) => AsyncRulePayload) + +export type BlurFunction = (args: BlurEvent) => void +export type ChangeFunction = (args: ChangeEvent) => void +export type FocusFunction = (args: FocusEvent) => void + export interface FieldProps { - rule?: RegExp | ((args: RuleParameters) => boolean) - asyncRule?: RegExp | ((args: RuleParameters) => AsyncRulePayload) + rule?: Rule + asyncRule?: AsyncRule required?: boolean skip?: boolean - onBlur?: (args: BlurEvent) => void - onChange?: (args: ChangeEvent) => void - onFocus?: (args: FocusEvent) => void -} - -export interface CreateFieldState { + onBlur?: BlurFunction + onChange?: ChangeFunction + onFocus?: FocusFunction +} + +export interface FieldState { + asyncRule?: AsyncRule + controlled: boolean + debounceValidate: () => any // ? + errors: string[] | null + expected: boolean + fieldGroup?: string // ? + fieldPath: string[] + focused: boolean + getRef: any // ? + initialValue: string + invalid: boolean + mapValue: () => any // ? + onBlur?: BlurFunction + onChange?: ChangeFunction + onFocus?: FocusFunction + pendingAsyncValidation?: boolean // ? + reactiveProps: any // ? required: boolean - validating: boolean - validatedSync: boolean - validatedAsync: boolean + rule?: Rule + serialize: () => string + skip?: boolean + type: string valid: boolean - validSync: boolean validAsync: boolean - invalid: boolean - errors?: string[] + validatedAsync: boolean + validatedSync: boolean + validating: boolean + validSync: boolean + value: string | number + valuePropName: string } export interface CreateFieldProps { fieldProps: any - fieldState: CreateFieldState + fieldState: FieldState } type FieldPreset = From 2dbd8c8bf7159da5aaeaeccfcaf0086a2ee27c79 Mon Sep 17 00:00:00 2001 From: ell10t Date: Tue, 30 Oct 2018 11:28:12 +0000 Subject: [PATCH 03/14] Corrects action response type --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index c63156bf..656679c6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -42,7 +42,7 @@ type SubmitFormEvent = ( export interface FormProps { innerRef?: () => void - action: () => void | Promise + action: () => Promise initialValues?: InitialValues /* Validation */ From 8462878d65bdf1f12f8b69cca6ea499ff9a0f1db Mon Sep 17 00:00:00 2001 From: ell10t Date: Tue, 30 Oct 2018 11:55:04 +0000 Subject: [PATCH 04/14] Adds missing form props --- index.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.d.ts b/index.d.ts index 656679c6..f798bb98 100644 --- a/index.d.ts +++ b/index.d.ts @@ -41,6 +41,10 @@ type SubmitFormEvent = ( ) => void export interface FormProps { + id?: string + className?: string + style?: React.StyleHTMLAttributes + innerRef?: () => void action: () => Promise initialValues?: InitialValues From c7f7743b9c68ff2ab02f809da726738c74af6c75 Mon Sep 17 00:00:00 2001 From: ell10t Date: Tue, 30 Oct 2018 16:18:16 +0000 Subject: [PATCH 05/14] Adds interface for action func arguments --- index.d.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/index.d.ts b/index.d.ts index f798bb98..abb38c02 100644 --- a/index.d.ts +++ b/index.d.ts @@ -36,17 +36,20 @@ export interface ValidationMessages { type GenericFormEvent = (args: { fields: Fields; form: Form }) => void -type SubmitFormEvent = ( - args: { serialized: SerializedForm; fields: Fields; form: Form }, -) => void +type FormSubmitState = { + serialized: SerializedForm; + fields: Fields; + form: Form +} + +type SubmitFormEvent = (args: FormSubmitState) => void; export interface FormProps { id?: string className?: string - style?: React.StyleHTMLAttributes - + style?: React.CSSProperties; innerRef?: () => void - action: () => Promise + action: (args: FormSubmitState) => Promise; initialValues?: InitialValues /* Validation */ From c0c032f9a9ae7b4bfb377b75371109b0210d62a3 Mon Sep 17 00:00:00 2001 From: ell10t Date: Tue, 30 Oct 2018 16:18:46 +0000 Subject: [PATCH 06/14] Adds ValidationSchema values --- index.d.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index abb38c02..770460d2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -28,7 +28,9 @@ export interface Fields { [key: string]: Field | Fields } -export interface ValidationSchema {} +export interface ValidationSchema { + [key: string]: Rule | ValidationSchema; +} export interface ValidationMessages { [key: string]: string | string[] | ValidationMessages From 9fb30459702c30b9a00d84e2cbe3252a6b871c72 Mon Sep 17 00:00:00 2001 From: ell10t Date: Wed, 31 Oct 2018 10:23:15 +0000 Subject: [PATCH 07/14] Improves vaidation message types/interface --- index.d.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index 770460d2..f3782bde 100644 --- a/index.d.ts +++ b/index.d.ts @@ -29,29 +29,45 @@ export interface Fields { } export interface ValidationSchema { - [key: string]: Rule | ValidationSchema; + [key: string]: Rule | ValidationSchema +} + +export interface ValidationMessage { + [key: string]: { + invalid?: string + required?: string + rule?: { + [key: string]: string + } + } +} + +export interface ValidationMessageGroup { + [key: string]: ValidationMessage } export interface ValidationMessages { - [key: string]: string | string[] | ValidationMessages + type?: ValidationMessage + name?: ValidationMessage | ValidationMessageGroup + general?: ValidationMessage } type GenericFormEvent = (args: { fields: Fields; form: Form }) => void type FormSubmitState = { - serialized: SerializedForm; - fields: Fields; + serialized: SerializedForm + fields: Fields form: Form } -type SubmitFormEvent = (args: FormSubmitState) => void; +type SubmitFormEvent = (args: FormSubmitState) => void export interface FormProps { id?: string className?: string - style?: React.CSSProperties; + style?: React.CSSProperties innerRef?: () => void - action: (args: FormSubmitState) => Promise; + action: (args: FormSubmitState) => Promise initialValues?: InitialValues /* Validation */ From b9ea7fe484b8b48ce0d5af88142d50027a8e8f3f Mon Sep 17 00:00:00 2001 From: ell10t Date: Wed, 31 Oct 2018 10:38:01 +0000 Subject: [PATCH 08/14] Adds generic input props type for fieldProps --- index.d.ts | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/index.d.ts b/index.d.ts index f3782bde..e83eeadd 100644 --- a/index.d.ts +++ b/index.d.ts @@ -32,7 +32,7 @@ export interface ValidationSchema { [key: string]: Rule | ValidationSchema } -export interface ValidationMessage { +export interface ValidationMessageSet { [key: string]: { invalid?: string required?: string @@ -43,13 +43,13 @@ export interface ValidationMessage { } export interface ValidationMessageGroup { - [key: string]: ValidationMessage + [key: string]: ValidationMessageSet } export interface ValidationMessages { - type?: ValidationMessage - name?: ValidationMessage | ValidationMessageGroup - general?: ValidationMessage + type?: ValidationMessageSet + name?: ValidationMessageSet | ValidationMessageGroup + general?: ValidationMessageSet } type GenericFormEvent = (args: { fields: Fields; form: Form }) => void @@ -69,18 +69,16 @@ export interface FormProps { innerRef?: () => void action: (args: FormSubmitState) => Promise initialValues?: InitialValues - /* Validation */ rules?: ValidationSchema messages?: ValidationMessages - /* Callbacks */ onFirstChange?: ( args: { event: React.FormEvent nextValue: string prevValue: string - fieldProps: object + fieldProps: React.InputHTMLAttributes fields: Fields form: Form }, @@ -107,8 +105,8 @@ export class Form extends React.Component { export interface RuleParameters { get: (path: string[]) => string | undefined - value: any - fieldProps: any + value: string + fieldProps: React.InputHTMLAttributes fields: Fields form: Form } @@ -116,13 +114,13 @@ export interface RuleParameters { export type AsyncRulePayload = { valid: boolean extra?: { - [exraKey: string]: any + [exraKey: string]: any // ? } } interface Event { event: React.FormEvent - fieldProps: any + fieldProps: React.InputHTMLAttributes fields: Fields form: Form } @@ -132,8 +130,8 @@ export interface FocusEvent extends Event {} export interface ChangeEvent extends Event { event: React.FormEvent - prevValue: any - nextValue: any + prevValue: string + nextValue: string } export type Rule = RegExp | ((args: RuleParameters) => boolean) @@ -153,7 +151,7 @@ export interface FieldProps { onFocus?: FocusFunction } -export interface FieldState { +export interface CreateFieldState { asyncRule?: AsyncRule controlled: boolean debounceValidate: () => any // ? @@ -187,8 +185,8 @@ export interface FieldState { } export interface CreateFieldProps { - fieldProps: any - fieldState: FieldState + fieldProps: React.InputHTMLAttributes + fieldState: CreateFieldState } type FieldPreset = From ee8851e1eebd1296d1dfd7216d845634b6a50c0d Mon Sep 17 00:00:00 2001 From: ell10t Date: Wed, 31 Oct 2018 10:43:25 +0000 Subject: [PATCH 09/14] Adds stricter typing for ValidationSchema --- index.d.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index e83eeadd..cb40a9d3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -28,8 +28,17 @@ export interface Fields { [key: string]: Field | Fields } +export interface ValidationRuleGroup { + [key: string]: Rule +} + export interface ValidationSchema { - [key: string]: Rule | ValidationSchema + type?: { + [key: string]: Rule + } + name?: { + [key: string]: Rule | ValidationRuleGroup + } } export interface ValidationMessageSet { From 067332d1d817196b0858f4ae2213988b6ccab159 Mon Sep 17 00:00:00 2001 From: ell10t Date: Wed, 31 Oct 2018 10:49:26 +0000 Subject: [PATCH 10/14] Fixes "missing" property name for ValidationMessageSet type --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index cb40a9d3..9f7b2896 100644 --- a/index.d.ts +++ b/index.d.ts @@ -44,7 +44,7 @@ export interface ValidationSchema { export interface ValidationMessageSet { [key: string]: { invalid?: string - required?: string + missing?: string rule?: { [key: string]: string } From a336ce9793ed8d57d79c13436e17827166f37c4b Mon Sep 17 00:00:00 2001 From: ell10t Date: Fri, 2 Nov 2018 10:10:55 +0000 Subject: [PATCH 11/14] Fixes fieldProps and error types --- index.d.ts | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/index.d.ts b/index.d.ts index 9f7b2896..be29799f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -61,6 +61,16 @@ export interface ValidationMessages { general?: ValidationMessageSet } +export type Errors = string[] | string | null + +export interface FieldErrors { + [key: string]: + | Errors + | { + [key: string]: Errors + } +} + type GenericFormEvent = (args: { fields: Fields; form: Form }) => void type FormSubmitState = { @@ -87,7 +97,7 @@ export interface FormProps { event: React.FormEvent nextValue: string prevValue: string - fieldProps: React.InputHTMLAttributes + fieldProps: FieldProps fields: Fields form: Form }, @@ -107,7 +117,7 @@ export interface FormProps { export class Form extends React.Component { reset: () => void serialize: () => SerializedForm - setErrors: FormErrors + setErrors: (errors: FieldErrors) => void submit: () => Promise validate: () => Promise } @@ -115,7 +125,7 @@ export class Form extends React.Component { export interface RuleParameters { get: (path: string[]) => string | undefined value: string - fieldProps: React.InputHTMLAttributes + fieldProps: FieldProps fields: Fields form: Form } @@ -129,7 +139,7 @@ export type AsyncRulePayload = { interface Event { event: React.FormEvent - fieldProps: React.InputHTMLAttributes + fieldProps: FieldProps fields: Fields form: Form } @@ -160,7 +170,7 @@ export interface FieldProps { onFocus?: FocusFunction } -export interface CreateFieldState { +export interface FieldState { asyncRule?: AsyncRule controlled: boolean debounceValidate: () => any // ? @@ -193,11 +203,6 @@ export interface CreateFieldState { valuePropName: string } -export interface CreateFieldProps { - fieldProps: React.InputHTMLAttributes - fieldState: CreateFieldState -} - type FieldPreset = | React.InputHTMLAttributes | React.InputHTMLAttributes @@ -211,10 +216,18 @@ export const fieldPresets: { textarea: React.TextareaHTMLAttributes } +export interface CreateFieldProps { + fieldProps: FieldProps + fieldState: FieldState +} + export function createField

( preset: FieldPreset, ): ( component: ( - props: CreateFieldProps & P, - ) => React.ReactElement, -) => React.ComponentClass + props: P & { + fieldProps: React.InputHTMLAttributes + fieldState: FieldState + }, + ) => React.ReactElement

, +) => React.ComponentClass

From decc14e2c311ebe705e04be92f001fea41e79aef Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Sat, 3 Nov 2018 11:03:16 +0100 Subject: [PATCH 12/14] Typings: Improves annotations for validation and components --- index.d.ts | 211 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 76 deletions(-) diff --git a/index.d.ts b/index.d.ts index be29799f..301942f6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -8,16 +8,23 @@ export interface FormProviderProps { export class FormProvider extends React.Component {} +/** + * Field.Group + */ +export interface FieldGroupProps { + name: string +} + export interface Field { - Group: React.ComponentClass<{ name: string }> + Group: React.ComponentClass } export interface InitialValues { [key: string]: string | InitialValues } -export interface SerializedForm { - [key: string]: string | SerializedForm +export interface SerializedFields { + [key: string]: string | SerializedFields } export interface FormErrors { @@ -25,28 +32,60 @@ export interface FormErrors { } export interface Fields { - [key: string]: Field | Fields + [fieldPath: string]: FieldState | Fields +} + +/** + * Validation schema + */ +export interface ResolverArgs { + get: (fieldPath: string[]) => any + value: string + fieldProps: FieldState + fields: Fields + form: Form +} + +export type Resolver = (args: ResolverArgs) => boolean + +export type AsyncRulePayload = { + valid: boolean + extra?: { + [exraKey: string]: any + } } -export interface ValidationRuleGroup { - [key: string]: Rule +export interface ResolverGroup { + [ruleName: string]: Resolver } export interface ValidationSchema { type?: { - [key: string]: Rule + [key: string]: Resolver | ResolverGroup } name?: { - [key: string]: Rule | ValidationRuleGroup + [key: string]: Resolver | ResolverGroup } } +/** + * Validation messages + */ +export interface MessageResolverArgs { + extra?: Object + fieldProps: FieldState + fields: Fields + form: Form +} + +export type MessageResolver = (args: MessageResolverArgs) => string | string + export interface ValidationMessageSet { [key: string]: { - invalid?: string - missing?: string + invalid?: MessageResolver + missing?: MessageResolver rule?: { - [key: string]: string + [ruleName: string]: MessageResolver } } } @@ -56,9 +95,9 @@ export interface ValidationMessageGroup { } export interface ValidationMessages { + general?: ValidationMessageSet type?: ValidationMessageSet name?: ValidationMessageSet | ValidationMessageGroup - general?: ValidationMessageSet } export type Errors = string[] | string | null @@ -71,75 +110,85 @@ export interface FieldErrors { } } -type GenericFormEvent = (args: { fields: Fields; form: Form }) => void +type GenericFormPayload = (args: { fields: Fields; form: Form }) => void -type FormSubmitState = { - serialized: SerializedForm +type FormSubmitPayload = { + serialized: SerializedFields fields: Fields form: Form } -type SubmitFormEvent = (args: FormSubmitState) => void +type SubmitFormEvent = (args: FormSubmitPayload) => void export interface FormProps { id?: string className?: string style?: React.CSSProperties - innerRef?: () => void - action: (args: FormSubmitState) => Promise + innerRef?: (ref: Form) => void + action: (args: FormSubmitPayload) => Promise /** @todo */ initialValues?: InitialValues + /* Validation */ rules?: ValidationSchema messages?: ValidationMessages + /* Callbacks */ - onFirstChange?: ( - args: { - event: React.FormEvent - nextValue: string - prevValue: string - fieldProps: FieldProps + onFirstChange?(args: { + event: React.FormEvent + nextValue: any /** @todo Field value generics */ + prevValue: any + fieldProps: FieldState /** @todo */ + fields: Fields + form: Form + }): void + onClear?: GenericFormPayload + onReset?: GenericFormPayload + onSerialize?( + args: GenericFormPayload & { serialized: SerializedFields }, + ): void + onInvalid?( + args: GenericFormPayload & { + invalidFields: Fields fields: Fields form: Form }, - ) => void - onClear?: GenericFormEvent - onReset?: GenericFormEvent - onSerialize?: (args: { serialized: SerializedForm }) => void - onInvalid?: ( - args: { invalidFields: Fields; fields: Fields; form: Form }, - ) => void + ): void onSubmitStart?: SubmitFormEvent onSubmitted?: SubmitFormEvent onSubmitFailed?: SubmitFormEvent onSubmitEnd?: SubmitFormEvent } -export class Form extends React.Component { - reset: () => void - serialize: () => SerializedForm - setErrors: (errors: FieldErrors) => void - submit: () => Promise - validate: () => Promise -} - -export interface RuleParameters { - get: (path: string[]) => string | undefined - value: string - fieldProps: FieldProps +export interface FormState { fields: Fields - form: Form + applicableRules: Object /** @todo */ + dirty: boolean } -export type AsyncRulePayload = { - valid: boolean - extra?: { - [exraKey: string]: any // ? - } +export class Form extends React.Component { + /* Private */ + private withRegisteredField + private registerField + private updateFieldsWith + private unregisterField + private handleFieldFocus + private handleFieldChange + private handleFieldBlur + private validateField + + /* Public */ + clear(predicate?: (fieldState: Field) => boolean): void + reset(predicate?: (fieldState: Field) => boolean): void + validate(predicate?: (fieldState: Field) => boolean): Promise + serialize(): SerializedFields + setValues(fieldsPatch: SerializedFields): void + setErrors(errors: FieldErrors): void + submit(): Promise /** @todo */ } interface Event { event: React.FormEvent - fieldProps: FieldProps + fieldProps: FieldState fields: Fields form: Form } @@ -153,8 +202,8 @@ export interface ChangeEvent extends Event { nextValue: string } -export type Rule = RegExp | ((args: RuleParameters) => boolean) -export type AsyncRule = RegExp | ((args: RuleParameters) => AsyncRulePayload) +export type Rule = RegExp | ((args: ResolverArgs) => boolean) +export type AsyncRule = RegExp | ((args: ResolverArgs) => AsyncRulePayload) export type BlurFunction = (args: BlurEvent) => void export type ChangeFunction = (args: ChangeEvent) => void @@ -170,27 +219,28 @@ export interface FieldProps { onFocus?: FocusFunction } -export interface FieldState { +/** @todo Value generic */ +export interface FieldState { asyncRule?: AsyncRule controlled: boolean - debounceValidate: () => any // ? + debounceValidate: () => any /** @todo */ errors: string[] | null expected: boolean - fieldGroup?: string // ? + fieldGroup?: string /** @todo */ fieldPath: string[] focused: boolean - getRef: any // ? + getRef: any /** @todo */ initialValue: string invalid: boolean - mapValue: () => any // ? + mapValue: (value: V) => any onBlur?: BlurFunction onChange?: ChangeFunction onFocus?: FocusFunction - pendingAsyncValidation?: boolean // ? - reactiveProps: any // ? + pendingAsyncValidation?: boolean /** @todo */ + reactiveProps: any /** @todo */ required: boolean rule?: Rule - serialize: () => string + serialize: (value: V) => string skip?: boolean type: string valid: boolean @@ -199,35 +249,44 @@ export interface FieldState { validatedSync: boolean validating: boolean validSync: boolean - value: string | number + value: V valuePropName: string } -type FieldPreset = - | React.InputHTMLAttributes - | React.InputHTMLAttributes - | React.TextareaHTMLAttributes - export const fieldPresets: { - checkbox: React.InputHTMLAttributes - input: React.InputHTMLAttributes - radio: React.InputHTMLAttributes - select: React.InputHTMLAttributes - textarea: React.TextareaHTMLAttributes + checkbox: CreateFieldOptions + input: CreateFieldOptions + radio: CreateFieldOptions + select: CreateFieldOptions + textarea: CreateFieldOptions } -export interface CreateFieldProps { - fieldProps: FieldProps - fieldState: FieldState +export interface CreateFieldOptions { + allowMultiple?: boolean + valuePropName?: ValuePropName + initialValue?: any + beforeRegister: () => any /** @todo */ + shouldValidateOnMount: ( + args: { + valuePropName: ValuePropName /** @todo [valuePropName] dynamic property */ + fieldRecord: any + context: any + }, + ) => boolean + mapPropsToField: () => any /** @todo */ + enforceProps: () => Object /** @todo */ + assertValue: (value: V) => boolean + mapValue: (value: any) => any + serialize: (value: V) => any } export function createField

( - preset: FieldPreset, + options: CreateFieldOptions, ): ( component: ( props: P & { fieldProps: React.InputHTMLAttributes - fieldState: FieldState + fieldState: FieldState }, ) => React.ReactElement

, ) => React.ComponentClass

From eb242c3ac786f42ddd716f85c4fdbe2321afd076 Mon Sep 17 00:00:00 2001 From: ell10t Date: Sat, 3 Nov 2018 10:42:36 +0000 Subject: [PATCH 13/14] Typings: Adds missing touched FieldState --- index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.d.ts b/index.d.ts index 301942f6..1a7a8db2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -242,6 +242,7 @@ export interface FieldState { rule?: Rule serialize: (value: V) => string skip?: boolean + touched: boolean type: string valid: boolean validAsync: boolean From ebce16e9dab124c76c52e73ab54cac51181e1205 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Thu, 8 Nov 2018 12:33:13 +0100 Subject: [PATCH 14/14] Typings: Adds explanatory comments for better DX --- index.d.ts | 145 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 39 deletions(-) diff --git a/index.d.ts b/index.d.ts index 1a7a8db2..5d088825 100644 --- a/index.d.ts +++ b/index.d.ts @@ -13,6 +13,7 @@ export class FormProvider extends React.Component {} */ export interface FieldGroupProps { name: string + exact?: boolean } export interface Field { @@ -38,33 +39,46 @@ export interface Fields { /** * Validation schema */ -export interface ResolverArgs { - get: (fieldPath: string[]) => any +export interface RuleResolverArgs { + /** + * Reactive field prop getter. + * Subscribes to the given field's prop path and resolves + * each time the prop value has changed. + */ + get: (fieldPropPath: string[]) => any value: string fieldProps: FieldState fields: Fields form: Form } -export type Resolver = (args: ResolverArgs) => boolean +export type RuleResolver = (args: RuleResolverArgs) => boolean export type AsyncRulePayload = { + /** + * Determines whether a field is valid based on the + * asynchronous validation result. + */ valid: boolean + /** + * Map of extra parameters returned from the asynchronous validation. + * Accessible under validation messages resolvers. + */ extra?: { [exraKey: string]: any } } -export interface ResolverGroup { - [ruleName: string]: Resolver +export interface RuleResolverGroup { + [ruleName: string]: RuleResolver } export interface ValidationSchema { type?: { - [key: string]: Resolver | ResolverGroup + [rulePath: string]: RuleResolver | RuleResolverGroup } name?: { - [key: string]: Resolver | ResolverGroup + [rulePath: string]: RuleResolver | RuleResolverGroup } } @@ -78,9 +92,9 @@ export interface MessageResolverArgs { form: Form } -export type MessageResolver = (args: MessageResolverArgs) => string | string +export type MessageResolver = string | ((args: MessageResolverArgs) => string) -export interface ValidationMessageSet { +export interface ValidationMessagesTypes { [key: string]: { invalid?: MessageResolver missing?: MessageResolver @@ -90,14 +104,14 @@ export interface ValidationMessageSet { } } -export interface ValidationMessageGroup { - [key: string]: ValidationMessageSet +export interface ValidationMessagesGroup { + [messagePath: string]: ValidationMessagesTypes } export interface ValidationMessages { - general?: ValidationMessageSet - type?: ValidationMessageSet - name?: ValidationMessageSet | ValidationMessageGroup + general?: ValidationMessagesTypes + type?: ValidationMessagesTypes + name?: ValidationMessagesTypes | ValidationMessagesGroup } export type Errors = string[] | string | null @@ -110,7 +124,7 @@ export interface FieldErrors { } } -type GenericFormPayload = (args: { fields: Fields; form: Form }) => void +type FormGenericPayload = (args: { fields: Fields; form: Form }) => void type FormSubmitPayload = { serialized: SerializedFields @@ -141,13 +155,13 @@ export interface FormProps { fields: Fields form: Form }): void - onClear?: GenericFormPayload - onReset?: GenericFormPayload + onClear?: FormGenericPayload + onReset?: FormGenericPayload onSerialize?( - args: GenericFormPayload & { serialized: SerializedFields }, + args: FormGenericPayload & { serialized: SerializedFields }, ): void onInvalid?( - args: GenericFormPayload & { + args: FormGenericPayload & { invalidFields: Fields fields: Fields form: Form @@ -186,61 +200,82 @@ export class Form extends React.Component { submit(): Promise /** @todo */ } -interface Event { +interface FormGenericEvent { event: React.FormEvent fieldProps: FieldState fields: Fields form: Form } -export interface BlurEvent extends Event {} -export interface FocusEvent extends Event {} +export interface BlurEvent extends FormGenericEvent {} +export interface FocusEvent extends FormGenericEvent {} -export interface ChangeEvent extends Event { +export interface ChangeEvent extends FormGenericEvent { event: React.FormEvent prevValue: string nextValue: string } -export type Rule = RegExp | ((args: ResolverArgs) => boolean) -export type AsyncRule = RegExp | ((args: ResolverArgs) => AsyncRulePayload) +export type Rule = RegExp | ((args: RuleResolverArgs) => boolean) +export type AsyncRule = RegExp | ((args: RuleResolverArgs) => AsyncRulePayload) -export type BlurFunction = (args: BlurEvent) => void -export type ChangeFunction = (args: ChangeEvent) => void -export type FocusFunction = (args: FocusEvent) => void +export type BlurHandler = (args: BlurEvent) => void +export type ChangeHandler = (args: ChangeEvent) => void +export type FocusHandler = (args: FocusEvent) => void +/** + * Map derived from the field's state, that is being assigned to the + * field element (i.e. input, select, etc.). + */ export interface FieldProps { - rule?: Rule - asyncRule?: AsyncRule + rule?: Rule /** @todo shouldn't be here */ + asyncRule?: AsyncRule /** @todo shouldn't be here */ required?: boolean - skip?: boolean - onBlur?: BlurFunction - onChange?: ChangeFunction - onFocus?: FocusFunction + skip?: boolean /** @todo shouldn't be here */ + onBlur?: BlurHandler + onChange?: ChangeHandler + onFocus?: FocusHandler } /** @todo Value generic */ export interface FieldState { + /** + * Asynchronous field rule resolver. + */ asyncRule?: AsyncRule controlled: boolean debounceValidate: () => any /** @todo */ + /** + * List of the applicable error messages. + */ errors: string[] | null + /** + * Determines whether the current value of this field + * is expected by the relevant validation rules. + */ expected: boolean fieldGroup?: string /** @todo */ fieldPath: string[] focused: boolean - getRef: any /** @todo */ + getRef: () => any /** @todo */ initialValue: string invalid: boolean mapValue: (value: V) => any - onBlur?: BlurFunction - onChange?: ChangeFunction - onFocus?: FocusFunction + onBlur?: BlurHandler + onChange?: ChangeHandler + onFocus?: FocusHandler pendingAsyncValidation?: boolean /** @todo */ reactiveProps: any /** @todo */ required: boolean + /** + * Synchornous field rule resolver. + */ rule?: Rule serialize: (value: V) => string + /** + * Determines whether to skip this field during the fields serialization + * regardless of its validity or value. + */ skip?: boolean touched: boolean type: string @@ -263,10 +298,27 @@ export const fieldPresets: { } export interface CreateFieldOptions { + /** + * Allows multiple instance of this field to be registered + * with the same name within a single form context (Form, Field.Group). + */ allowMultiple?: boolean + + /** + * Property name that stores the value of this field. + */ valuePropName?: ValuePropName + + /** + * Initial value of all the instances of this field. + * This has lower priority than "MyField.props.initialValue" + */ initialValue?: any beforeRegister: () => any /** @todo */ + + /** + * Determines whether a field must be validated upon mounting. + */ shouldValidateOnMount: ( args: { valuePropName: ValuePropName /** @todo [valuePropName] dynamic property */ @@ -274,10 +326,25 @@ export interface CreateFieldOptions { context: any }, ) => boolean - mapPropsToField: () => any /** @todo */ + mapPropsToField: () => Object /** @todo */ enforceProps: () => Object /** @todo */ + + /** + * A predicate function that determines whether a field contains any value. + * Useful for the fields with the custom value instance (i.e. Object). + */ assertValue: (value: V) => boolean + + /** + * Custom mapping function applied whenever a field receives a "raw" value. + * Useful when the internal value instance of a field has a different data type + * than its initial value. This has no effect over the internal value updates. + */ mapValue: (value: any) => any + + /** + * Custom transformer function applied to the serialized field's value. + */ serialize: (value: V) => any }