Skip to content

Commit 28c4c52

Browse files
Feature/form validator (#34)
* Renamed from base-validator -> base-field-validator. * Added BaseValidator. * Refactored props interface naming. * Added ValidateForm in value helpers. Refactored. * Added Origin to FormErorr. * Updated FormStoreState with new FormState. * Error property has now flag. * Validating and Error from fields flags w/ tests. * Added ValidateForm. * Added FormStateProps. * Started making tests for subscriber validation form. * Updated contracts index in simplr-forms-core. * Update subscriber ValidateForm and cascading in ValidateField. * Moved default ValidationType to Form prop fieldsValidationType. * Error -> HasError. * Added GetInitialStateProperties method. * Fixed error: Error -> HasError. * Added form-store test: Touched false after value updated to identical one.
1 parent f268ef4 commit 28c4c52

33 files changed

+516
-225
lines changed

packages/simplr-forms-core/__tests__/stores/form-store.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,65 @@ describe("Form store", () => {
337337
}
338338
}
339339
});
340+
341+
describe("state properties", () => {
342+
it("pristine false after field value changed", () => {
343+
const fieldId = "field id";
344+
345+
formStore.RegisterField(fieldId, "");
346+
expect(formStore.GetState().Pristine).toBe(true);
347+
348+
formStore.UpdateFieldValue(fieldId, "next value");
349+
expect(formStore.GetState().Pristine).toBe(false);
350+
});
351+
352+
it("touched true after field value changed", () => {
353+
const fieldId = "field id";
354+
355+
formStore.RegisterField(fieldId, "");
356+
expect(formStore.GetState().Touched).toBe(false);
357+
358+
formStore.UpdateFieldValue(fieldId, "next value");
359+
expect(formStore.GetState().Touched).toBe(true);
360+
});
361+
362+
it("touched false after value updated to identical one", () => {
363+
const fieldId = "field id";
364+
365+
formStore.RegisterField(fieldId, "");
366+
expect(formStore.GetState().Touched).toBe(false);
367+
368+
formStore.UpdateFieldValue(fieldId, "");
369+
expect(formStore.GetState().Touched).toBe(false);
370+
});
371+
372+
it("error true is after field error ", async done => {
373+
const fieldId = "field id";
374+
try {
375+
formStore.RegisterField(fieldId, "");
376+
expect(formStore.GetState().HasError).toBe(false);
377+
378+
await formStore.ValidateField(fieldId, new Promise<void>((resolve, reject) => {
379+
reject("error message");
380+
}));
381+
expect(formStore.GetState().HasError).toBe(true);
382+
} catch (error) {
383+
done.fail(error);
384+
}
385+
386+
done();
387+
});
388+
389+
it("validating true after field error ", () => {
390+
const fieldId = "field id";
391+
formStore.RegisterField(fieldId, "");
392+
expect(formStore.GetState().Validating).toBe(false);
393+
394+
formStore.ValidateField(fieldId, new Promise<void>((resolve, reject) => {
395+
reject("error message");
396+
}));
397+
398+
expect(formStore.GetState().Validating).toBe(true);
399+
});
400+
});
340401
});

packages/simplr-forms-core/src/abstractions/base-form.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from "react";
22
import * as PropTypes from "prop-types";
33

44
import * as FormContracts from "../contracts/form";
5+
import { FieldValidationType } from "../contracts/validation";
56
import { FormStore } from "../stores/form-store";
67
import { FSHContainer, FormStoresHandler } from "../stores/form-stores-handler";
78

@@ -20,6 +21,9 @@ export abstract class BaseForm<TProps extends FormContracts.FormProps, TState> e
2021

2122
static defaultProps: FormContracts.FormProps = {
2223
destroyOnUnmount: true,
24+
fieldsValidationType: FieldValidationType.OnFieldRegistered |
25+
FieldValidationType.OnValueChange |
26+
FieldValidationType.OnPropsChange,
2327
disabled: false
2428
};
2529

packages/simplr-forms-core/src/abstractions/core-field.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import * as PropTypes from "prop-types";
55
import {
66
CoreFieldProps,
77
FieldValue,
8-
FieldValidationType,
98
FieldFormatValueCallback,
109
FieldNormalizeValueCallback,
1110
FieldParseValueCallback,
@@ -18,6 +17,7 @@ import { FormStoreStateRecord } from "../contracts/form-store";
1817
import * as FormStoreActions from "../actions/form-store";
1918
// import { FieldsGroupContextProps } from "../contracts/fields-group";
2019
import { FSHContainer } from "../stores/form-stores-handler";
20+
import { FieldValidationType } from "../contracts/validation";
2121

2222
export interface CoreFieldState {
2323
Field?: FieldStateRecord;
@@ -47,9 +47,6 @@ export abstract class CoreField<TProps extends CoreFieldProps, TState extends Co
4747
static defaultProps: CoreFieldProps = {
4848
// Empty string checked to have value in componentWillMount
4949
name: "",
50-
validationType: FieldValidationType.OnFieldRegistered |
51-
FieldValidationType.OnValueChange |
52-
FieldValidationType.OnPropsChange,
5350
// By default, fields data should be retained, even if the field is unmounted
5451
destroyOnUnmount: false
5552
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1+
import { TypedRecord } from "typed-immutable-record/dist";
12
// Future self might love usage of an object with message property instead of a plain string
3+
24
export interface FormError {
35
Message: string;
6+
// Told you so.
7+
Origin?: FormErrorOrigin;
8+
}
9+
10+
export interface FormErrorRecord extends TypedRecord<FormErrorRecord>, FormError { }
11+
12+
export enum FormErrorOrigin {
13+
None,
14+
Validation,
15+
Submit
416
}

packages/simplr-forms-core/src/contracts/field.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TypedRecord } from "typed-immutable-record";
2-
import { FormError } from "./error";
2+
import { FormErrorRecord } from "./error";
3+
import { FieldValidationType } from "./validation";
34

45
// Field value can be of any type or undefined
56
export type FieldValue = any | undefined;
@@ -43,11 +44,3 @@ export type FieldStateProps = CoreFieldProps & React.Props<any>;
4344

4445
export interface FieldStateRecord extends TypedRecord<FieldStateRecord>, FieldState { }
4546
export interface FieldStatePropsRecord extends TypedRecord<FieldStatePropsRecord>, FieldStateProps { }
46-
export interface FormErrorRecord extends TypedRecord<FormErrorRecord>, FormError { }
47-
48-
export enum FieldValidationType {
49-
None,
50-
OnFieldRegistered = 1 << 1,
51-
OnValueChange = 1 << 2,
52-
OnPropsChange = 1 << 3
53-
}

packages/simplr-forms-core/src/contracts/form-store.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ import { TypedRecord } from "typed-immutable-record";
33

44
import { FieldStateRecord } from "../contracts/field";
55
import { FieldsGroupStoreState } from "../contracts/fields-group";
6-
import { FormStateRecord, FormPropsRecord } from "../contracts/form";
6+
import { FormStateRecord } from "../contracts/form";
77

8-
export interface FormStoreState {
8+
export interface FormStoreState extends FormStoreStateStatus {
99
Fields: Immutable.Map<string, FieldStateRecord>;
1010
FieldsGroups: Immutable.Map<string, FieldsGroupStoreState>;
1111
Form: FormStateRecord;
12-
FormProps: FormPropsRecord;
12+
}
13+
14+
export interface FormStoreStateStatus {
15+
Validating: boolean;
16+
HasError: boolean;
17+
Pristine: boolean;
18+
Touched: boolean;
1319
}
1420

1521
export interface FormStoreStateRecord extends TypedRecord<FormStoreStateRecord>, FormStoreState { }

packages/simplr-forms-core/src/contracts/form.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
11
import { TypedRecord } from "typed-immutable-record";
22

3-
import { FormError } from "./error";
43
import { FormStore } from "../stores/form-store";
4+
import { FormErrorRecord } from "./error";
5+
import { FieldValidationType } from "./validation";
56

67
export interface FormProps {
78
formId?: string;
89
formStore?: FormStore;
910
destroyOnUnmount?: boolean;
1011
forceSubmit?: boolean;
1112
disabled?: boolean;
13+
fieldsValidationType?: FieldValidationType;
14+
formValidationType?: FieldValidationType;
1215
}
1316

14-
export interface FormPropsRecord extends TypedRecord<FormPropsRecord>, FormProps { }
17+
export type FormStateProps = FormProps & React.Props<any>;
18+
19+
export interface FormPropsRecord extends TypedRecord<FormPropsRecord>, FormStateProps { }
1520

1621
export interface FormChildContext {
1722
FormId: string;
1823
}
1924

2025
export interface FormState {
26+
Props: FormStateProps;
2127
Validating: boolean;
22-
Pristine: boolean;
23-
Error?: FormError;
28+
Error?: FormErrorRecord;
29+
SubmitCallback?: () => void;
2430
Submitting: boolean;
25-
Touched: boolean;
2631
SuccessfullySubmitted: boolean;
27-
SubmitCallback?: () => void;
2832
ActiveFieldId?: string;
2933
}
3034

packages/simplr-forms-core/src/contracts/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from "./fields-group";
44
export * from "./form-store";
55
export * from "./form";
66
export * from "./value";
7+
export * from "./validation";
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export enum FieldValidationType {
2+
None,
3+
OnFieldRegistered = 1 << 1,
4+
OnValueChange = 1 << 2,
5+
OnPropsChange = 1 << 3
6+
}

0 commit comments

Comments
 (0)