Skip to content

Commit 4186033

Browse files
Martynas ŽilinskasDovydasNavickas
authored andcommitted
Added validation skip if value is not newest (expired).
1 parent 165587a commit 4186033

File tree

20 files changed

+370
-21
lines changed

20 files changed

+370
-21
lines changed

docs/2017-04/2017-04-08.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ Mind blown? :tada: I hoped so. :clap:
153153

154154
There are more things to cover here, but we are already hyped-up to get our hands dirty and start coding.
155155

156+
------------
157+
156158
The feedback and questions are more than welcome!
157159

158160
------------

docs/2017-04/2017-04-14.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
## April 14 ([discuss](https://github.com/SimplrJS/simplr-forms/pull/4))
2+
3+
### Attendees
4+
5+
* [Dovydas](https://twitter.com/dovydasnav) (QuatroDev)
6+
* [Martynas](https://twitter.com/MartiogalaLT) (QuatroDev)
7+
* [Giedrius](https://twitter.com/giedrucis) (QuatroDev)
8+
* [Aurimas](https://twitter.com/waikys) (QuatroDev)
9+
10+
### simplr-forms-core is done for pre-alpha
11+
12+
During the week, we worked on `simplr-forms-core` to have a solid foundation for `simplr-forms-dom` and `simplr-forms-native`.
13+
14+
Most of the coding was done by Dovydas and Martynas, with valuable and fast reviews by Giedrius.
15+
Feels good to work with such an amazing team.
16+
17+
Also, Aurimas joined our efforts by preparing react-native app for development of `simplr-forms-native`. Thanks, Aurimas!
18+
19+
We will start working on both `dom` and `native` packages soon.
20+
21+
### So... What happened during the week?
22+
23+
We can confidently say that we have a solid first appearances of `core`, which consists of:
24+
* [`form-stores-handler`][1] - a general manager for all forms data stores
25+
* [`form-store`][2] - a data store for one particular form
26+
* [`base-form`][3] - a base component for a form to register to store and provide context for children, register with `form-stores-handler`, etc.
27+
* [`base-field`][4] - a base component for field that is aware of the parent form, registers and registers to and from form store, incorporates React lifecycle events, etc.
28+
29+
These 4 pretty much cover the base of the whole library.
30+
31+
Of course, there is more to it.
32+
33+
[`form-stores-handler`][1] and [`base-form`][3] extend [`ActionEmitter`][5] for you to be able to `addListener`
34+
for actions and know when the store is created, updates it's state or even more specific [actions][6],
35+
e.g. [value][7] or [props][8] are updated.
36+
37+
This enables another amazing thing: now we can externalize validation!
38+
39+
> Shoutout to Giedrius for publishing [`ActionEmitter`][5] right on time.
40+
> And for us all to debugging it in a single day :smile:
41+
42+
### Externalized validation
43+
44+
We understand that most developers use whatever they get out-of-the-box,
45+
especially if what they get is an easy-to-consume and also an optimal solution.
46+
47+
But sometimes usage of project-specific validation library is a must.
48+
49+
For example, there are quite a few industries with very specific data structures and their validations.
50+
Medicine, aviation, insurance, finance... And that's just a few on the tip of the tongue.
51+
52+
Therefore, externalizing validation is an amazing step forward to using `simplr-forms`
53+
no matter what data validation requirements you have.
54+
55+
### Testing
56+
57+
A library without tests these days is a scary and unstable bomb with a timer that shows gibberish on it's timer,
58+
i.e. you don't know when it is gonna hit you.
59+
60+
Therefore, we already have 44 tests in 4 test suites.
61+
62+
And we will write as much more as we need to ensure everything will progress forward
63+
and this library becomes de-facto a standard, a no brainer when anyone thinks about React and forms.
64+
65+
### What's next?
66+
67+
Releasing pre-alpha of `simplr-forms-core` and starting development of `simplr-forms-dom` and (maybe) `simplr-forms-native`.
68+
69+
Also, `simplr-validation` is coming into the light as we evaluate all of the peaces for it.
70+
71+
And now... It's coding time again! :tada:
72+
73+
------------
74+
75+
The feedback and questions are more than welcome!
76+
77+
------------
78+
79+
Please feel free to discuss these notes in the [corresponding pull request](https://github.com/SimplrJS/simplr-forms/pull/4).
80+
81+
[1]: https://github.com/SimplrJS/simplr-forms/blob/dev/packages/simplr-forms-core/src/stores/form-stores-handler.ts
82+
[2]: https://github.com/SimplrJS/simplr-forms/blob/dev/packages/simplr-forms-core/src/stores/form-store.ts
83+
[3]: https://github.com/SimplrJS/simplr-forms/blob/dev/packages/simplr-forms-core/src/abstractions/base-form.ts
84+
[4]: https://github.com/SimplrJS/simplr-forms/blob/dev/packages/simplr-forms-core/src/abstractions/base-field.ts
85+
[5]: https://github.com/SimplrJS/action-emitter
86+
[6]: https://github.com/SimplrJS/simplr-forms/tree/dev/packages/simplr-forms-core/src/actions
87+
[7]: https://github.com/SimplrJS/simplr-forms/blob/dev/packages/simplr-forms-core/src/actions/form-store-actions.ts#L3-L9
88+
[8]: https://github.com/SimplrJS/simplr-forms/blob/dev/packages/simplr-forms-core/src/actions/form-store-actions.ts#L11-L17

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,49 @@ describe("Form store", () => {
157157
}
158158
});
159159

160+
it("skip validation when newValue has expired", async (done) => {
161+
const formId = "FORM-ID";
162+
const fieldId = "FIELD-ID";
163+
const initialValue = "INITIAL-VALUE";
164+
const formStore = new FormStore(formId);
165+
const formError = "field error";
166+
167+
formStore.RegisterField(fieldId, initialValue);
168+
const validationPromise = new Promise<void>((resolve, reject) => {
169+
setTimeout(() => {
170+
reject(formError);
171+
}, 50);
172+
});
173+
174+
formStore.Validate(fieldId, validationPromise);
175+
176+
// Imitate removal of last letter
177+
formStore.ValueChanged(fieldId, initialValue.slice(0, initialValue.length - 1));
178+
179+
try {
180+
expect(formStore.GetField(fieldId).Validating).toBe(true);
181+
} catch (error) {
182+
done.fail(error);
183+
}
184+
185+
try {
186+
await validationPromise;
187+
} catch (err) {
188+
// Validation promise was rejected
189+
// Expects in the subsequent try block
190+
}
191+
192+
try {
193+
// It SHOULD still validating to be true because validation was skipped of expired value
194+
expect(formStore.GetField(fieldId).Validating).toBe(true);
195+
expect(formStore.GetField(fieldId).Error).toBeUndefined();
196+
} catch (err) {
197+
done.fail(err);
198+
}
199+
200+
done();
201+
});
202+
160203
it("registers field with props", () => {
161204
const formId = "FORM-ID";
162205
const fieldId = "FIELD-ID";

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ export abstract class BaseField<TProps extends FieldProps, TState extends BaseFi
4646
static defaultProps: FieldProps = {
4747
// Empty string checked to have value in componentWillMount
4848
name: "",
49-
validationType: FieldValidationType.OnChange,
49+
validationType: FieldValidationType.OnFieldRegistered |
50+
FieldValidationType.OnValueChange |
51+
FieldValidationType.OnPropsChange,
5052
// By default, fields data should be retained, even if the field is unmounted
5153
destroyOnUnmount: false
5254
};

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ export interface FieldStateRecord extends TypedRecord<FieldStateRecord>, FieldSt
3838
export interface FieldStatePropsRecord extends TypedRecord<FieldStatePropsRecord>, FieldStateProps { }
3939
export interface FormErrorRecord extends TypedRecord<FormErrorRecord>, FormError { }
4040

41-
export enum FieldValidationType {
41+
export const enum FieldValidationType {
4242
None,
43-
OnChange,
44-
OnBlur
43+
OnFieldRegistered = 1 << 1,
44+
OnValueChange = 1 << 2,
45+
OnPropsChange = 1 << 3
4546
}

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,20 +139,24 @@ export class FormStore extends ActionEmitter {
139139
const field = this.State.Fields.get(fieldId);
140140
const fieldValue = field.Value;
141141

142-
this.State = this.State.withMutations(state => {
143-
const fieldState = state.Fields.get(fieldId);
144-
state.Fields = state.Fields.set(fieldId, fieldState.merge({
145-
Validating: true,
146-
Error: undefined
147-
} as FieldState));
148-
});
142+
// Skip if it's already validating
143+
if (!field.Validating) {
144+
this.State = this.State.withMutations(state => {
145+
const fieldState = state.Fields.get(fieldId);
146+
state.Fields = state.Fields.set(fieldId, fieldState.merge({
147+
Validating: true,
148+
Error: undefined
149+
} as FieldState));
150+
});
151+
}
149152

150153
try {
151154
// Wait for validation to finish
152155
await validationPromise;
153156

154157
// Skip validation if the value has changed again
155-
if (field.Value !== fieldValue) {
158+
const currentFieldValue = this.State.Fields.get(fieldId).Value;
159+
if (currentFieldValue !== fieldValue) {
156160
return;
157161
}
158162

@@ -164,7 +168,8 @@ export class FormStore extends ActionEmitter {
164168
});
165169
} catch (error) {
166170
// Skip validation if the value has changed again
167-
if (field.Value !== fieldValue) {
171+
const currentFieldValue = this.State.Fields.get(fieldId).Value;
172+
if (currentFieldValue !== fieldValue) {
168173
return;
169174
}
170175

packages/simplr-validation/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
"@types/enzyme": "^2.7.7",
1515
"@types/jest": "^19.2.2",
1616
"@types/sinon": "^2.1.2",
17-
"enzyme": "^2.8.0",
17+
"enzyme": "^2.8.2",
1818
"jest": "^19.0.2",
19-
"jest-enzyme": "^3.0.0",
20-
"react-addons-test-utils": "^15.5.1",
19+
"jest-enzyme": "^3.0.1",
2120
"react-dom": "^15.5.4",
21+
"react-test-renderer": "^15.5.4",
2222
"sinon": "^2.1.0",
2323
"ts-jest": "^19.0.8",
2424
"tslint": "^5.0.0",
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import * as Abstractions from "./abstractions/index";
22
import * as Contracts from "./contracts";
3-
4-
export * from "./validators/index";
3+
import * as Validators from "./validators/index";
54

65
export {
76
Abstractions,
8-
Contracts
7+
Contracts,
8+
Validators
99
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as Validator from "validator";
2+
import { Contracts } from "simplr-forms-core";
3+
4+
import { BaseValidator, ValidatorProps } from "../abstractions/base-validator";
5+
import { ValidationResult } from "../contracts";
6+
7+
export interface AlphaValidatorProps extends ValidatorProps {
8+
locale?: ValidatorJS.AlphaLocale;
9+
}
10+
11+
export class AlphaValidator extends BaseValidator<AlphaValidatorProps> {
12+
Validate(value: Contracts.FieldValue): ValidationResult {
13+
if (this.SkipValidation(value)) {
14+
return this.ValidSync();
15+
}
16+
17+
if (!Validator.isAlpha(value, this.props.locale)) {
18+
return this.InvalidSync(this.props.errorMessage);
19+
}
20+
21+
return this.ValidSync();
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as Validator from "validator";
2+
import { Contracts } from "simplr-forms-core";
3+
4+
import { BaseValidator, ValidatorProps } from "../abstractions/base-validator";
5+
import { ValidationResult } from "../contracts";
6+
7+
export interface AlphanumericValidatorProps extends ValidatorProps {
8+
locale?: ValidatorJS.AlphanumericLocale;
9+
}
10+
11+
export class AlphanumericValidator extends BaseValidator<AlphanumericValidatorProps> {
12+
Validate(value: Contracts.FieldValue): ValidationResult {
13+
if (this.SkipValidation(value)) {
14+
return this.ValidSync();
15+
}
16+
17+
if (!Validator.isAlphanumeric(value, this.props.locale)) {
18+
return this.InvalidSync(this.props.errorMessage);
19+
}
20+
21+
return this.ValidSync();
22+
}
23+
}

0 commit comments

Comments
 (0)