Skip to content

Commit af44d7c

Browse files
author
Martynas Žilinskas
authored
Feature/error template (#98)
* Added template error rendering. * Updated listeners test. * Fixed ValidateValue function. * Updated travis file. * Update validation.test.tsx
1 parent 93decbd commit af44d7c

File tree

9 files changed

+119
-44
lines changed

9 files changed

+119
-44
lines changed

.travis.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ notifications:
55
on_failure: change
66
node_js:
77
- stable
8-
before_install:
9-
- "npm install -g npm@^5.0.3"
10-
- "npm install -g typescript@^2.3"
118
script:
129
- npm run generate
1310
- npm run tools-build

packages/react-forms-validation/__tests__/subscribers/form-store-subscriber.test.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
FieldRegistered,
1212
FieldPropsChanged,
1313
ValueChanged,
14+
FieldBlurred
1415
} from "@simplr/react-forms/actions";
1516

1617
import { BaseFormValidator, BaseFormValidatorProps } from "../../src/abstractions/base-form-validator";
@@ -73,13 +74,15 @@ describe("FormStoreSubscriber", () => {
7374
expect(formStore.listeners(FieldRegistered).length).toBe(0);
7475
expect(formStore.listeners(FieldPropsChanged).length).toBe(0);
7576
expect(formStore.listeners(ValueChanged).length).toBe(0);
77+
expect(formStore.listeners(FieldBlurred).length).toBe(0);
7678

7779
new FormStoreSubscriber(formStore);
7880
expect(callback.called).toEqual(true);
7981

8082
expect(formStore.listeners(FieldRegistered).length).toBe(1);
8183
expect(formStore.listeners(FieldPropsChanged).length).toBe(1);
8284
expect(formStore.listeners(ValueChanged).length).toBe(1);
85+
expect(formStore.listeners(FieldBlurred).length).toBe(1);
8386
});
8487

8588
it("remove listeners from form store", () => {
@@ -92,7 +95,7 @@ describe("FormStoreSubscriber", () => {
9295
expect(formStore.listeners(FieldRegistered).length).toBe(0);
9396
expect(formStore.listeners(FieldPropsChanged).length).toBe(0);
9497
expect(formStore.listeners(ValueChanged).length).toBe(0);
95-
98+
expect(formStore.listeners(FieldBlurred).length).toBe(0);
9699
});
97100

98101
describe("field validation", () => {

packages/react-forms-validation/__tests__/validation.test.tsx

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,78 @@
11
import * as React from "react";
22

3+
import { FormStore } from "@simplr/react-forms/stores";
4+
35
import * as Validation from "../src/utils/validation";
46
import { ContainsValidator } from "../src/validators/index";
7+
import { ValidationFieldErrorTemplate } from "../src/contracts";
8+
import * as Sinon from "sinon";
9+
import { FormError } from "@simplr/react-forms/contracts";
10+
11+
it("Validates value without errors", async done => {
12+
try {
13+
const errorMessage = "error message";
14+
const validValue = "p-ok";
15+
const fieldId = "field-id";
16+
const formStore = new FormStore("form-id");
17+
18+
const children = [
19+
<ContainsValidator value="ok" error={errorMessage} />,
20+
<input type="text" />
21+
];
22+
const validationPromise = Validation.ValidateField(children, validValue, fieldId, formStore);
23+
let validationResponse;
24+
25+
validationResponse = await validationPromise;
26+
expect(validationResponse).toBeUndefined();
27+
28+
done();
29+
} catch (error) {
30+
done.fail(error);
31+
}
32+
});
533

6-
it("Validate value without errors", async (done) => {
34+
it("Validates value with error", async done => {
735
const errorMessage = "error message";
8-
const validValue = "p-ok";
36+
const invalidValue = "invalid-value";
37+
const fieldId = "field-id";
38+
const formStore = new FormStore("form-id");
39+
940
const children = [
1041
<ContainsValidator value="ok" error={errorMessage} />,
1142
<input type="text" />
1243
];
13-
const validationPromise = Validation.ValidateField(children, validValue);
14-
let validationResponse;
44+
const validationPromise = Validation.ValidateField(children, invalidValue, fieldId, formStore);
45+
let validationResponse: FormError | undefined = undefined;
1546

1647
try {
17-
validationResponse = await validationPromise;
48+
await validationPromise;
1849
} catch (error) {
19-
done.fail(error);
50+
validationResponse = error;
2051
}
2152

2253
try {
23-
expect(validationResponse).toBeUndefined();
54+
expect(validationResponse).toBeDefined();
55+
expect(validationResponse!.Message).toBe(errorMessage);
2456
done();
2557
} catch (error) {
2658
done.fail(error);
2759
}
2860
});
2961

30-
it("Validate value with error", async (done) => {
31-
const errorMessage = "error message";
32-
const invvalidValue = "invalid-value";
62+
it("Validates value with error template", async done => {
63+
const invalidValue = "invalid-value";
64+
const fieldId = "field-id";
65+
const formStore = new FormStore("form-id");
66+
const errorTemplate: ValidationFieldErrorTemplate = (tFieldId, tFormStore) =>
67+
`${tFieldId} error template`;
68+
const spyCallback = Sinon.spy(errorTemplate);
69+
3370
const children = [
34-
<ContainsValidator value="ok" error={errorMessage} />,
71+
<ContainsValidator value="ok" error={spyCallback} />,
3572
<input type="text" />
3673
];
37-
const validationPromise = Validation.ValidateField(children, invvalidValue);
38-
let validationResponse;
74+
const validationPromise = Validation.ValidateField(children, invalidValue, fieldId, formStore);
75+
let validationResponse: FormError | undefined = undefined;
3976

4077
try {
4178
await validationPromise;
@@ -44,7 +81,9 @@ it("Validate value with error", async (done) => {
4481
}
4582

4683
try {
47-
expect(validationResponse).toBe(errorMessage);
84+
expect(spyCallback.called).toBe(true);
85+
expect(validationResponse).toBeDefined();
86+
expect(validationResponse!.Message).toBe(errorTemplate(fieldId, formStore));
4887
done();
4988
} catch (error) {
5089
done.fail(error);

packages/react-forms-validation/src/abstractions/base-field-validator.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { ValidationError } from "../contracts";
1+
import { ValidationError, ValidationFieldErrorTemplate } from "../contracts";
22
import { BaseValidator, BaseValidatorProps } from "./base-validator";
33

4-
export type BaseFieldValidatorProps = BaseValidatorProps;
4+
export interface BaseFieldValidatorProps extends BaseValidatorProps {
5+
error: ValidationError<ValidationFieldErrorTemplate>;
6+
}
57

68
export abstract class BaseFieldValidator<TProps extends BaseFieldValidatorProps>
79
extends BaseValidator<TProps, {}> {

packages/react-forms-validation/src/abstractions/base-form-validator.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { ValidationError } from "../contracts";
1+
import { ValidationError, ValidationFormErrorTemplate } from "../contracts";
22
import { BaseValidator, BaseValidatorProps } from "./base-validator";
33

4-
export type BaseFormValidatorProps = BaseValidatorProps;
4+
export interface BaseFormValidatorProps extends BaseValidatorProps {
5+
error: ValidationError<ValidationFormErrorTemplate>;
6+
}
57

68
export abstract class BaseFormValidator<TProps extends BaseFormValidatorProps>
79
extends BaseValidator<TProps, {}> {

packages/react-forms-validation/src/abstractions/base-validator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export abstract class BaseValidator<TProps extends BaseValidatorProps, TState>
2222
});
2323
}
2424

25-
protected async Invalid(error: string): Promise<void> {
25+
protected async Invalid(error: ValidationError): Promise<void> {
2626
return new Promise<void>((resolve, reject) => {
2727
reject(error);
2828
});

packages/react-forms-validation/src/contracts.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { FormError } from "@simplr/react-forms/contracts";
2+
import { FormStore } from "@simplr/react-forms/stores";
23

34
export interface Validator {
45
Validate(value: any): ValidationResult;
56
}
67

7-
export type ValidationError = string | FormError;
8+
export type ValidationError<TTemplateFunc = Function> = string | FormError | TTemplateFunc;
9+
10+
export type ValidationFormErrorTemplate = (formStore: FormStore) => ValidationError;
11+
export type ValidationFieldErrorTemplate = (fieldId: string, formStore: FormStore) => ValidationError;
812

913
export type ValidationResult = Promise<void> | ValidationError | undefined;
1014

packages/react-forms-validation/src/subscribers/form-store-subscriber.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class FormStoreSubscriber {
7373
}
7474

7575
const childrenArray = React.Children.toArray(fieldProps.children) as JSX.Element[];
76-
const validationPromise = ValidateField(childrenArray, fieldState.Value);
76+
const validationPromise = ValidateField(childrenArray, fieldState.Value, fieldId, this.formStore);
7777
await this.formStore.ValidateField(fieldId, validationPromise);
7878
}
7979

@@ -88,7 +88,7 @@ export class FormStoreSubscriber {
8888
}
8989

9090
const childrenArray = React.Children.toArray(formProps.children) as JSX.Element[];
91-
const validationPromise = ValidateForm(childrenArray, this.formStore.ToObject());
91+
const validationPromise = ValidateForm(childrenArray, this.formStore.ToObject(), this.formStore);
9292
await this.formStore.ValidateForm(validationPromise);
9393
}
9494

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,80 @@
11
import { FieldValue, FormErrorOrigin } from "@simplr/react-forms/contracts";
22
import {
3-
ProcessValue,
43
IsComponentOfType,
54
RenderComponents,
65
ConstructFormError
76
} from "@simplr/react-forms/utils";
7+
import { FormStore } from "@simplr/react-forms/stores";
88

99
import {
1010
Validator,
1111
FIELD_VALIDATOR_FUNCTION_NAME,
12-
FORM_VALIDATOR_FUNCTION_NAME
12+
FORM_VALIDATOR_FUNCTION_NAME,
13+
ValidationFieldErrorTemplate,
14+
ValidationFormErrorTemplate,
15+
ValidationResult,
16+
ValidationError
1317
} from "../contracts";
1418

1519
function IsPromise<T>(value: any): value is Promise<T> {
1620
return value != null && value.then != null && value.catch != null;
1721
}
1822

23+
function IsFunction<T>(value: any): value is T {
24+
return typeof value === "function";
25+
}
26+
1927
export async function ValidateValue(
2028
components: JSX.Element[],
2129
value: any,
22-
validatorTypeFunctionName: string): Promise<void> {
30+
validatorTypeFunctionName: string,
31+
errorProccessor: (error: ValidationResult) => ValidationResult
32+
): Promise<void> {
2333
const validators = components.filter(x => IsComponentOfType(x, validatorTypeFunctionName));
2434
const renderedValidators = RenderComponents<Validator>(validators);
2535

2636
for (const validator of renderedValidators) {
2737
const validationResult = validator.Validate(value);
2838
// Ensure that we have a promise
29-
let promise: Promise<void>;
39+
let validationError: ValidationError | undefined;
3040
if (IsPromise<void>(validationResult)) {
31-
promise = validationResult;
41+
try {
42+
await validationResult;
43+
} catch (caughtError) {
44+
validationError = caughtError;
45+
}
3246
} else {
33-
promise = new Promise<void>((resolve, reject) => {
34-
const error = ConstructFormError(validationResult, FormErrorOrigin.Validation);
35-
if (error !== undefined) {
36-
reject(validationResult);
37-
return;
38-
}
39-
resolve();
40-
});
47+
validationError = validationResult;
48+
}
49+
50+
const builtError = errorProccessor(validationError);
51+
const error = ConstructFormError(builtError, FormErrorOrigin.Validation);
52+
if (error !== undefined) {
53+
throw error;
4154
}
42-
await promise;
4355
}
4456
}
4557

46-
export async function ValidateField(components: JSX.Element[], value: FieldValue): Promise<void> {
47-
return ValidateValue(components, value, FIELD_VALIDATOR_FUNCTION_NAME);
58+
export async function ValidateField(components: JSX.Element[], value: FieldValue, fieldId: string, formStore: FormStore): Promise<void> {
59+
return ValidateValue(components, value, FIELD_VALIDATOR_FUNCTION_NAME, error => {
60+
if (IsFunction<ValidationFieldErrorTemplate>(error)) {
61+
const errorTemplate = error;
62+
const builtError = errorTemplate(fieldId, formStore);
63+
return builtError;
64+
}
65+
66+
return error;
67+
});
4868
}
4969

50-
export async function ValidateForm(components: JSX.Element[], value: any): Promise<void> {
51-
return ValidateValue(components, value, FORM_VALIDATOR_FUNCTION_NAME);
70+
export async function ValidateForm(components: JSX.Element[], value: any, formStore: FormStore): Promise<void> {
71+
return ValidateValue(components, value, FORM_VALIDATOR_FUNCTION_NAME, error => {
72+
if (IsFunction<ValidationFormErrorTemplate>(error)) {
73+
const errorTemplate = error;
74+
const builtError = errorTemplate(formStore);
75+
return builtError;
76+
}
77+
78+
return error;
79+
});
5280
}

0 commit comments

Comments
 (0)