@homebound/form-state 2.18.1 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FormStateApp.d.ts +2 -3
- package/dist/FormStateApp.js +8 -47
- package/dist/FormStateApp.test.js +14 -14
- package/dist/config.d.ts +6 -6
- package/dist/fields/fragmentField.js +3 -3
- package/dist/fields/listField.js +12 -12
- package/dist/fields/objectField.d.ts +4 -4
- package/dist/fields/objectField.js +10 -10
- package/dist/fields/valueField.d.ts +13 -0
- package/dist/fields/valueField.js +49 -14
- package/dist/formState.stories.d.ts +2 -4
- package/dist/formState.stories.js +1 -1
- package/dist/formState.test.js +171 -77
- package/dist/formStateDomain.js +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +0 -11
- package/dist/proxies.d.ts +5 -0
- package/dist/proxies.js +21 -0
- package/dist/rules.d.ts +1 -1
- package/dist/rules.js +1 -1
- package/dist/setupTests.js +2 -2
- package/dist/useFormState.d.ts +4 -4
- package/dist/useFormState.js +13 -17
- package/dist/useFormState.test.js +141 -198
- package/dist/useFormStates.d.ts +3 -3
- package/dist/useFormStates.js +7 -11
- package/dist/useFormStates.test.js +56 -89
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +8 -8
- package/dist/utils.test.js +49 -49
- package/package.json +44 -39
- package/dist/AutoSaveStatus/AutoSaveStatusProvider.d.ts +0 -24
- package/dist/AutoSaveStatus/AutoSaveStatusProvider.js +0 -91
- package/dist/AutoSaveStatus/index.d.ts +0 -2
- package/dist/AutoSaveStatus/index.js +0 -9
- package/dist/AutoSaveStatus/useAutoSaveStatus.d.ts +0 -1
- package/dist/AutoSaveStatus/useAutoSaveStatus.js +0 -9
- package/dist/AutoSaveStatus/useAutoSaveStatus.test.d.ts +0 -1
- package/dist/AutoSaveStatus/useAutoSaveStatus.test.js +0 -152
package/dist/formState.test.js
CHANGED
|
@@ -4,24 +4,61 @@ const mobx_1 = require("mobx");
|
|
|
4
4
|
const objectField_1 = require("./fields/objectField");
|
|
5
5
|
const formStateDomain_1 = require("./formStateDomain");
|
|
6
6
|
const rules_1 = require("./rules");
|
|
7
|
-
describe("
|
|
7
|
+
describe("mobx behavior", () => {
|
|
8
|
+
it("mobx batches if in actions", () => {
|
|
9
|
+
const a = (0, mobx_1.observable)({ name: "a1" });
|
|
10
|
+
const b = (0, mobx_1.observable)({ name: "b1" });
|
|
11
|
+
const map = new mobx_1.ObservableMap();
|
|
12
|
+
map.set("a", a);
|
|
13
|
+
map.set("b", b);
|
|
14
|
+
let runs = 0;
|
|
15
|
+
(0, mobx_1.autorun)(() => {
|
|
16
|
+
noop([...map.values()].filter((v) => v.name.includes("1")));
|
|
17
|
+
runs++;
|
|
18
|
+
});
|
|
19
|
+
expect(runs).toBe(1);
|
|
20
|
+
// When we change both a and b
|
|
21
|
+
(0, mobx_1.runInAction)(() => {
|
|
22
|
+
a.name = "a11";
|
|
23
|
+
b.name = "b2";
|
|
24
|
+
});
|
|
25
|
+
// Then the reaction waited and only ran once
|
|
26
|
+
expect(runs).toBe(2);
|
|
27
|
+
});
|
|
28
|
+
it("mobx can watch undefined keys", () => {
|
|
29
|
+
const a = new ObservableObject();
|
|
30
|
+
const b = new ObservableObject();
|
|
31
|
+
const map = new mobx_1.ObservableMap();
|
|
32
|
+
map.set("a", a);
|
|
33
|
+
map.set("b", b);
|
|
34
|
+
let runs = 0;
|
|
35
|
+
(0, mobx_1.autorun)(() => {
|
|
36
|
+
noop([...map.values()].map((a) => a.age));
|
|
37
|
+
runs++;
|
|
38
|
+
});
|
|
39
|
+
expect(runs).toBe(1);
|
|
40
|
+
b.age = 1;
|
|
41
|
+
expect(runs).toBe(2);
|
|
42
|
+
});
|
|
8
43
|
it("mobx lists maintain observable identity", () => {
|
|
9
44
|
// given a parent observable
|
|
10
|
-
const a = mobx_1.observable({ list: [] });
|
|
45
|
+
const a = (0, mobx_1.observable)({ list: [] });
|
|
11
46
|
// if we observable-ize a value being pushing it on the list
|
|
12
|
-
const c1 = mobx_1.observable({});
|
|
47
|
+
const c1 = (0, mobx_1.observable)({});
|
|
13
48
|
a.list.push(c1);
|
|
14
49
|
// then we get identify equality on the list lookups
|
|
15
50
|
expect(a.list[0] === c1).toEqual(true);
|
|
16
51
|
});
|
|
52
|
+
});
|
|
53
|
+
describe("formState", () => {
|
|
17
54
|
it("can create a simple object", () => {
|
|
18
|
-
const a = objectField_1.createObjectState({ title: { type: "value", rules: [rules_1.required] } }, {});
|
|
55
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value", rules: [rules_1.required] } }, {});
|
|
19
56
|
expect(a.valid).toBeFalsy();
|
|
20
57
|
});
|
|
21
58
|
it("can validate a simple input", () => {
|
|
22
|
-
const a = objectField_1.createObjectState({ title: { type: "value", rules: [rules_1.required] } }, { title: "b1" });
|
|
59
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value", rules: [rules_1.required] } }, { title: "b1" });
|
|
23
60
|
let numErrors = 0;
|
|
24
|
-
mobx_1.autorun(() => {
|
|
61
|
+
(0, mobx_1.autorun)(() => {
|
|
25
62
|
numErrors = a.title.errors.length;
|
|
26
63
|
});
|
|
27
64
|
expect(a.valid).toBeTruthy();
|
|
@@ -33,7 +70,7 @@ describe("formState", () => {
|
|
|
33
70
|
expect(a.errors).toEqual(["title: Required"]);
|
|
34
71
|
});
|
|
35
72
|
it("can tell what is required", () => {
|
|
36
|
-
const a = objectField_1.createObjectState({
|
|
73
|
+
const a = (0, objectField_1.createObjectState)({
|
|
37
74
|
title: { type: "value", rules: [rules_1.required] },
|
|
38
75
|
isPublished: { type: "value" },
|
|
39
76
|
}, {});
|
|
@@ -41,9 +78,9 @@ describe("formState", () => {
|
|
|
41
78
|
expect(a.isPublished.required).toBeFalsy();
|
|
42
79
|
});
|
|
43
80
|
it("trims whitespace within the required rule", () => {
|
|
44
|
-
const a = objectField_1.createObjectState({ title: { type: "value", rules: [rules_1.required] } }, { title: "initial valid title" });
|
|
81
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value", rules: [rules_1.required] } }, { title: "initial valid title" });
|
|
45
82
|
let numErrors = 0;
|
|
46
|
-
mobx_1.autorun(() => {
|
|
83
|
+
(0, mobx_1.autorun)(() => {
|
|
47
84
|
numErrors = a.title.errors.length;
|
|
48
85
|
});
|
|
49
86
|
expect(a.valid).toBeTruthy();
|
|
@@ -56,16 +93,16 @@ describe("formState", () => {
|
|
|
56
93
|
expect(a.errors).toEqual(["title: Required"]);
|
|
57
94
|
});
|
|
58
95
|
it("can set values", () => {
|
|
59
|
-
const a = objectField_1.createObjectState({ title: { type: "value" } }, { title: "b1" });
|
|
96
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value" } }, { title: "b1" });
|
|
60
97
|
expect(a.title.value).toEqual("b1");
|
|
61
98
|
});
|
|
62
99
|
it("can read values", () => {
|
|
63
|
-
const a = objectField_1.createObjectState({ title: { type: "value" } }, { title: "b1" });
|
|
100
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value" } }, { title: "b1" });
|
|
64
101
|
expect(a.value.title).toEqual("b1");
|
|
65
102
|
expect(a.title.value).toEqual("b1");
|
|
66
103
|
});
|
|
67
104
|
it("can set dates", () => {
|
|
68
|
-
const a = objectField_1.createObjectState({
|
|
105
|
+
const a = (0, objectField_1.createObjectState)({
|
|
69
106
|
birthday: {
|
|
70
107
|
type: "value",
|
|
71
108
|
rules: [({ value }) => ((value === null || value === void 0 ? void 0 : value.getTime()) === formStateDomain_1.jan2.getTime() ? "cannot be born on jan2" : undefined)],
|
|
@@ -86,6 +123,35 @@ describe("formState", () => {
|
|
|
86
123
|
const a = createAuthorWithAddressInputState({ address: { city: "b1" } });
|
|
87
124
|
expect(a.address.city.value).toEqual("b1");
|
|
88
125
|
});
|
|
126
|
+
it("can adapt values", () => {
|
|
127
|
+
// Given an author where `delete` is normally a boolean
|
|
128
|
+
const a = (0, objectField_1.createObjectState)({
|
|
129
|
+
delete: { type: "value" },
|
|
130
|
+
}, { delete: true });
|
|
131
|
+
const boolField = a.delete;
|
|
132
|
+
// But we adapt it to a string
|
|
133
|
+
const stringField = a.delete.adapt({
|
|
134
|
+
toValue: (b) => String(b),
|
|
135
|
+
fromValue: (s) => Boolean(s),
|
|
136
|
+
});
|
|
137
|
+
// Then we can read it as a string
|
|
138
|
+
expect(stringField.value).toEqual("true");
|
|
139
|
+
// And we can set it as a string
|
|
140
|
+
stringField.value = "";
|
|
141
|
+
expect(boolField.value).toBe(false);
|
|
142
|
+
// And the originalValue is maintained
|
|
143
|
+
expect(boolField.originalValue).toBe(true);
|
|
144
|
+
expect(stringField.originalValue).toBe("true");
|
|
145
|
+
// As well as the dirty.
|
|
146
|
+
expect(boolField.dirty).toBe(true);
|
|
147
|
+
expect(stringField.dirty).toBe(true);
|
|
148
|
+
// And reverting works
|
|
149
|
+
stringField.revertChanges();
|
|
150
|
+
expect(boolField.dirty).toBe(false);
|
|
151
|
+
expect(stringField.dirty).toBe(false);
|
|
152
|
+
expect(boolField.value).toBe(true);
|
|
153
|
+
expect(stringField.value).toBe("true");
|
|
154
|
+
});
|
|
89
155
|
it("maintains object identity", () => {
|
|
90
156
|
const a1 = { firstName: "a1" };
|
|
91
157
|
const state = createAuthorInputState(a1);
|
|
@@ -113,7 +179,7 @@ describe("formState", () => {
|
|
|
113
179
|
const state = createAuthorInputState(a1);
|
|
114
180
|
let numBooks;
|
|
115
181
|
let ticks = 0;
|
|
116
|
-
mobx_1.autorun(() => {
|
|
182
|
+
(0, mobx_1.autorun)(() => {
|
|
117
183
|
numBooks = state.books.rows.length;
|
|
118
184
|
ticks++;
|
|
119
185
|
});
|
|
@@ -135,7 +201,7 @@ describe("formState", () => {
|
|
|
135
201
|
const state = createAuthorInputState(a1);
|
|
136
202
|
let books;
|
|
137
203
|
let ticks = 0;
|
|
138
|
-
mobx_1.autorun(() => {
|
|
204
|
+
(0, mobx_1.autorun)(() => {
|
|
139
205
|
books = state.books.value;
|
|
140
206
|
ticks++;
|
|
141
207
|
});
|
|
@@ -150,7 +216,7 @@ describe("formState", () => {
|
|
|
150
216
|
};
|
|
151
217
|
// And we initially have ids in the input
|
|
152
218
|
const a1 = { id: "1", firstName: "a1", books: [{ id: "2", title: "t1" }] };
|
|
153
|
-
const state = objectField_1.createObjectState(config, a1);
|
|
219
|
+
const state = (0, objectField_1.createObjectState)(config, a1);
|
|
154
220
|
// And we edit a few things
|
|
155
221
|
state.firstName.set("a2");
|
|
156
222
|
state.books.add({ title: "t2" });
|
|
@@ -168,7 +234,7 @@ describe("formState", () => {
|
|
|
168
234
|
expect(a1.valid).toBeTruthy();
|
|
169
235
|
let lastValid = undefined;
|
|
170
236
|
let ticks = 0;
|
|
171
|
-
mobx_1.autorun(() => {
|
|
237
|
+
(0, mobx_1.autorun)(() => {
|
|
172
238
|
lastValid = a1.valid;
|
|
173
239
|
ticks++;
|
|
174
240
|
});
|
|
@@ -271,7 +337,7 @@ describe("formState", () => {
|
|
|
271
337
|
const a1 = createAuthorInputState({ firstName: "a1", books: [] });
|
|
272
338
|
let ticks = 0;
|
|
273
339
|
let numErrors = 0;
|
|
274
|
-
mobx_1.autorun(() => {
|
|
340
|
+
(0, mobx_1.autorun)(() => {
|
|
275
341
|
numErrors = a1.books.errors.length;
|
|
276
342
|
ticks++;
|
|
277
343
|
});
|
|
@@ -287,7 +353,7 @@ describe("formState", () => {
|
|
|
287
353
|
expect(numErrors).toEqual(0);
|
|
288
354
|
});
|
|
289
355
|
it("can validate across fields", () => {
|
|
290
|
-
const a = objectField_1.createObjectState({
|
|
356
|
+
const a = (0, objectField_1.createObjectState)({
|
|
291
357
|
firstName: { type: "value", rules: [] },
|
|
292
358
|
lastName: { type: "value" },
|
|
293
359
|
}, {});
|
|
@@ -304,7 +370,7 @@ describe("formState", () => {
|
|
|
304
370
|
it("passes sanitized values to validation rules", () => {
|
|
305
371
|
// Given a field with a validation rule
|
|
306
372
|
const rule = jest.fn().mockReturnValue("Required");
|
|
307
|
-
const a = objectField_1.createObjectState({ firstName: { type: "value", rules: [rule] } }, {});
|
|
373
|
+
const a = (0, objectField_1.createObjectState)({ firstName: { type: "value", rules: [rule] } }, {});
|
|
308
374
|
expect(rule).toHaveBeenCalledTimes(0);
|
|
309
375
|
// When the field is set to empty string
|
|
310
376
|
a.firstName.value = "";
|
|
@@ -321,7 +387,7 @@ describe("formState", () => {
|
|
|
321
387
|
it("passes trimmed values to validation rules on maybeAutoSave", () => {
|
|
322
388
|
// Given we have a required rule
|
|
323
389
|
const rule = jest.fn().mockReturnValue("Required");
|
|
324
|
-
const a = objectField_1.createObjectState({ firstName: { type: "value", rules: [rule] } }, {});
|
|
390
|
+
const a = (0, objectField_1.createObjectState)({ firstName: { type: "value", rules: [rule] } }, {});
|
|
325
391
|
expect(rule).toHaveBeenCalledTimes(0);
|
|
326
392
|
// When the user initially sets an empty string value
|
|
327
393
|
a.firstName.focus();
|
|
@@ -348,12 +414,12 @@ describe("formState", () => {
|
|
|
348
414
|
});
|
|
349
415
|
});
|
|
350
416
|
it("simple value changes trigger observers", () => {
|
|
351
|
-
const a = objectField_1.createObjectState({
|
|
417
|
+
const a = (0, objectField_1.createObjectState)({
|
|
352
418
|
title: { type: "value", rules: [rules_1.required] },
|
|
353
419
|
}, { title: "t2" });
|
|
354
420
|
let lastTitle = undefined;
|
|
355
421
|
let ticks = 0;
|
|
356
|
-
mobx_1.autorun(() => {
|
|
422
|
+
(0, mobx_1.autorun)(() => {
|
|
357
423
|
lastTitle = a.title.value;
|
|
358
424
|
ticks++;
|
|
359
425
|
});
|
|
@@ -466,7 +532,7 @@ describe("formState", () => {
|
|
|
466
532
|
expect(maybeAutoSave).toBeCalledTimes(0);
|
|
467
533
|
});
|
|
468
534
|
it("knows list of primitives are dirty", () => {
|
|
469
|
-
const a1 = objectField_1.createObjectState({ favoriteColors: { type: "value" } }, {});
|
|
535
|
+
const a1 = (0, objectField_1.createObjectState)({ favoriteColors: { type: "value" } }, {});
|
|
470
536
|
expect(a1.favoriteColors.dirty).toBeFalsy();
|
|
471
537
|
a1.favoriteColors.set([formStateDomain_1.Color.Blue]);
|
|
472
538
|
expect(a1.dirty).toBeTruthy();
|
|
@@ -477,7 +543,7 @@ describe("formState", () => {
|
|
|
477
543
|
expect(a1.dirty).toBeFalsy();
|
|
478
544
|
});
|
|
479
545
|
it("knows list of primitives are dirty with initialized as empty list", () => {
|
|
480
|
-
const a1 = objectField_1.createObjectState({ favoriteColors: { type: "value" } }, { favoriteColors: [] });
|
|
546
|
+
const a1 = (0, objectField_1.createObjectState)({ favoriteColors: { type: "value" } }, { favoriteColors: [] });
|
|
481
547
|
expect(a1.favoriteColors.dirty).toBeFalsy();
|
|
482
548
|
a1.favoriteColors.set([formStateDomain_1.Color.Blue]);
|
|
483
549
|
expect(a1.dirty).toBeTruthy();
|
|
@@ -489,7 +555,7 @@ describe("formState", () => {
|
|
|
489
555
|
});
|
|
490
556
|
it("can evaluate arrays to be equal even if order of options are different from original when strictOrder = false", () => {
|
|
491
557
|
// Given an array field value where we define `strictOrder` as false
|
|
492
|
-
const a1 = objectField_1.createObjectState({ favoriteColors: { type: "value", strictOrder: false } }, { favoriteColors: [formStateDomain_1.Color.Red, formStateDomain_1.Color.Blue] });
|
|
558
|
+
const a1 = (0, objectField_1.createObjectState)({ favoriteColors: { type: "value", strictOrder: false } }, { favoriteColors: [formStateDomain_1.Color.Red, formStateDomain_1.Color.Blue] });
|
|
493
559
|
expect(a1.favoriteColors.dirty).toBeFalsy();
|
|
494
560
|
// When changing the order of the values
|
|
495
561
|
a1.favoriteColors.set([formStateDomain_1.Color.Blue, formStateDomain_1.Color.Red]);
|
|
@@ -498,7 +564,7 @@ describe("formState", () => {
|
|
|
498
564
|
});
|
|
499
565
|
it("evaluates arrays to be equal based on order by default", () => {
|
|
500
566
|
// Given an array field value
|
|
501
|
-
const a1 = objectField_1.createObjectState({ favoriteColors: { type: "value" } }, { favoriteColors: [formStateDomain_1.Color.Red, formStateDomain_1.Color.Blue] });
|
|
567
|
+
const a1 = (0, objectField_1.createObjectState)({ favoriteColors: { type: "value" } }, { favoriteColors: [formStateDomain_1.Color.Red, formStateDomain_1.Color.Blue] });
|
|
502
568
|
expect(a1.favoriteColors.dirty).toBeFalsy();
|
|
503
569
|
// When we change the order of the values
|
|
504
570
|
a1.favoriteColors.set([formStateDomain_1.Color.Blue, formStateDomain_1.Color.Red]);
|
|
@@ -675,7 +741,7 @@ describe("formState", () => {
|
|
|
675
741
|
expect(a1.value.firstName).toBeNull();
|
|
676
742
|
});
|
|
677
743
|
it("can map properties to other types", () => {
|
|
678
|
-
const a1 = objectField_1.createObjectState({
|
|
744
|
+
const a1 = (0, objectField_1.createObjectState)({
|
|
679
745
|
person: { type: "value" },
|
|
680
746
|
}, { person: { firstName: "a1", lastName: "b1" } });
|
|
681
747
|
a1.person.set({ firstName: "a2", lastName: "b2" });
|
|
@@ -701,7 +767,7 @@ describe("formState", () => {
|
|
|
701
767
|
});
|
|
702
768
|
it("maintain field readOnly state when form is readOnly", () => {
|
|
703
769
|
// Given a formState
|
|
704
|
-
const formState = objectField_1.createObjectState({ title: { type: "value", rules: [rules_1.required], readOnly: true } }, {});
|
|
770
|
+
const formState = (0, objectField_1.createObjectState)({ title: { type: "value", rules: [rules_1.required], readOnly: true } }, {});
|
|
705
771
|
// Then expect form
|
|
706
772
|
expect(formState.title.readOnly).toBeTruthy();
|
|
707
773
|
});
|
|
@@ -776,7 +842,7 @@ describe("formState", () => {
|
|
|
776
842
|
expect(a1.firstName.loading).toBeTruthy();
|
|
777
843
|
});
|
|
778
844
|
it("canSave returns dirty and touches", () => {
|
|
779
|
-
const a1 = objectField_1.createObjectState({
|
|
845
|
+
const a1 = (0, objectField_1.createObjectState)({
|
|
780
846
|
firstName: { type: "value", rules: [rules_1.required] },
|
|
781
847
|
}, {});
|
|
782
848
|
expect(a1.firstName.touched).toBeFalsy();
|
|
@@ -784,7 +850,7 @@ describe("formState", () => {
|
|
|
784
850
|
expect(a1.firstName.touched).toBeTruthy();
|
|
785
851
|
});
|
|
786
852
|
it("uses toJSON if available for dirty checks", () => {
|
|
787
|
-
const a1 = objectField_1.createObjectState({
|
|
853
|
+
const a1 = (0, objectField_1.createObjectState)({
|
|
788
854
|
birthday: { type: "value" },
|
|
789
855
|
}, { birthday: new formStateDomain_1.DateOnly(formStateDomain_1.jan1) });
|
|
790
856
|
a1.birthday.set(new formStateDomain_1.DateOnly(formStateDomain_1.jan1));
|
|
@@ -799,7 +865,7 @@ describe("formState", () => {
|
|
|
799
865
|
expect(a.valid).toBeTruthy();
|
|
800
866
|
});
|
|
801
867
|
it("can validate a list input", () => {
|
|
802
|
-
const a = objectField_1.createObjectState({
|
|
868
|
+
const a = (0, objectField_1.createObjectState)({
|
|
803
869
|
books: {
|
|
804
870
|
type: "list",
|
|
805
871
|
config: { title: { type: "value", rules: [rules_1.required] } },
|
|
@@ -807,7 +873,7 @@ describe("formState", () => {
|
|
|
807
873
|
},
|
|
808
874
|
}, {});
|
|
809
875
|
let lastErrors = "";
|
|
810
|
-
mobx_1.autorun(() => {
|
|
876
|
+
(0, mobx_1.autorun)(() => {
|
|
811
877
|
lastErrors = a.books.errors.join(", ");
|
|
812
878
|
});
|
|
813
879
|
a.books.add({ title: "t1" });
|
|
@@ -816,7 +882,7 @@ describe("formState", () => {
|
|
|
816
882
|
expect(lastErrors).toEqual("");
|
|
817
883
|
});
|
|
818
884
|
it("can ignore a deleted list entry", () => {
|
|
819
|
-
const a = objectField_1.createObjectState({
|
|
885
|
+
const a = (0, objectField_1.createObjectState)({
|
|
820
886
|
books: {
|
|
821
887
|
type: "list",
|
|
822
888
|
config: {
|
|
@@ -835,7 +901,7 @@ describe("formState", () => {
|
|
|
835
901
|
expect(a.books.valid).toBeTruthy();
|
|
836
902
|
});
|
|
837
903
|
it("can treat a list entry as read-only", () => {
|
|
838
|
-
const a = objectField_1.createObjectState({
|
|
904
|
+
const a = (0, objectField_1.createObjectState)({
|
|
839
905
|
books: {
|
|
840
906
|
type: "list",
|
|
841
907
|
config: {
|
|
@@ -864,13 +930,13 @@ describe("formState", () => {
|
|
|
864
930
|
a.address.city.value = "b1";
|
|
865
931
|
expect(a.address.city.value).toEqual("b1");
|
|
866
932
|
expect(a.address.value).toMatchInlineSnapshot(`
|
|
867
|
-
|
|
933
|
+
{
|
|
868
934
|
"city": "b1",
|
|
869
935
|
}
|
|
870
936
|
`);
|
|
871
937
|
expect(a.value).toMatchInlineSnapshot(`
|
|
872
|
-
|
|
873
|
-
"address":
|
|
938
|
+
{
|
|
939
|
+
"address": {
|
|
874
940
|
"city": "b1",
|
|
875
941
|
},
|
|
876
942
|
}
|
|
@@ -879,12 +945,12 @@ describe("formState", () => {
|
|
|
879
945
|
it("can wrap an existing list observable", () => {
|
|
880
946
|
// Given an array observable that is already created
|
|
881
947
|
const b1 = { title: "b1" };
|
|
882
|
-
const books = mobx_1.observable([b1]);
|
|
948
|
+
const books = (0, mobx_1.observable)([b1]);
|
|
883
949
|
// When we create a form state around it
|
|
884
950
|
const formState = createAuthorInputState({ books });
|
|
885
951
|
// Then the initial book was copied over
|
|
886
952
|
let lastLength = 0;
|
|
887
|
-
mobx_1.autorun(() => {
|
|
953
|
+
(0, mobx_1.autorun)(() => {
|
|
888
954
|
lastLength = formState.books.rows.length;
|
|
889
955
|
});
|
|
890
956
|
expect(lastLength).toEqual(1);
|
|
@@ -903,7 +969,7 @@ describe("formState", () => {
|
|
|
903
969
|
expect(config).toBeDefined();
|
|
904
970
|
});
|
|
905
971
|
it("can reset observable objects with computeds", () => {
|
|
906
|
-
const formState = objectField_1.createObjectState({
|
|
972
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
907
973
|
firstName: { type: "value" },
|
|
908
974
|
lastName: { type: "value" },
|
|
909
975
|
fullName: { type: "value", computed: true },
|
|
@@ -915,8 +981,35 @@ describe("formState", () => {
|
|
|
915
981
|
formState.revertChanges();
|
|
916
982
|
expect(formState.firstName.value).toEqual("first");
|
|
917
983
|
});
|
|
984
|
+
it("can observe observable objects being mutated directly", () => {
|
|
985
|
+
// Given a mobx class with a computed
|
|
986
|
+
const instance = new ObservableObject();
|
|
987
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
988
|
+
firstName: { type: "value" },
|
|
989
|
+
lastName: { type: "value" },
|
|
990
|
+
fullName: { type: "value", computed: true },
|
|
991
|
+
}, instance);
|
|
992
|
+
expect(formState.firstName.value).toEqual("first");
|
|
993
|
+
expect(formState.fullName.value).toEqual("first last");
|
|
994
|
+
// And we hook up reactivity to the computed
|
|
995
|
+
let numCalcs = 0;
|
|
996
|
+
(0, mobx_1.autorun)(() => {
|
|
997
|
+
numCalcs++;
|
|
998
|
+
noop(formState.fullName.value);
|
|
999
|
+
});
|
|
1000
|
+
// When the underlying mobx class is directly changed
|
|
1001
|
+
instance.firstName = "change";
|
|
1002
|
+
// Then our form-state computed re-run
|
|
1003
|
+
expect(numCalcs).toBe(2);
|
|
1004
|
+
expect(formState.fullName.value).toEqual("change last");
|
|
1005
|
+
// And when the underlying mobx class has a field unset
|
|
1006
|
+
instance.firstName = undefined;
|
|
1007
|
+
// Then our fomr-state computed re-runs again
|
|
1008
|
+
expect(numCalcs).toBe(3);
|
|
1009
|
+
expect(formState.fullName.value).toEqual("undefined last");
|
|
1010
|
+
});
|
|
918
1011
|
it("can set computeds that have setters", () => {
|
|
919
|
-
const formState = objectField_1.createObjectState({
|
|
1012
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
920
1013
|
firstName: { type: "value" },
|
|
921
1014
|
lastName: { type: "value" },
|
|
922
1015
|
fullName: { type: "value", computed: true },
|
|
@@ -929,7 +1022,7 @@ describe("formState", () => {
|
|
|
929
1022
|
expect(formState.firstName.value).toEqual("first");
|
|
930
1023
|
});
|
|
931
1024
|
it("can mark fields as read only", () => {
|
|
932
|
-
const formState = objectField_1.createObjectState({
|
|
1025
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
933
1026
|
firstName: { type: "value" },
|
|
934
1027
|
lastName: { type: "value" },
|
|
935
1028
|
fullName: { type: "value", readOnly: true },
|
|
@@ -943,7 +1036,7 @@ describe("formState", () => {
|
|
|
943
1036
|
});
|
|
944
1037
|
it("can return only changed primitive fields", () => {
|
|
945
1038
|
// Given an author
|
|
946
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, {
|
|
1039
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, {
|
|
947
1040
|
id: "a:1",
|
|
948
1041
|
firstName: "f",
|
|
949
1042
|
lastName: "l",
|
|
@@ -961,7 +1054,7 @@ describe("formState", () => {
|
|
|
961
1054
|
});
|
|
962
1055
|
it("can return only changed object fields", () => {
|
|
963
1056
|
// Given an author with an address object
|
|
964
|
-
const formState = objectField_1.createObjectState(authorWithAddressConfig, {
|
|
1057
|
+
const formState = (0, objectField_1.createObjectState)(authorWithAddressConfig, {
|
|
965
1058
|
id: "a:1",
|
|
966
1059
|
firstName: "f",
|
|
967
1060
|
address: { city: "c1", street: "s1" },
|
|
@@ -978,7 +1071,7 @@ describe("formState", () => {
|
|
|
978
1071
|
});
|
|
979
1072
|
it("changedValue skips empty nested fields", () => {
|
|
980
1073
|
// Given a new author with an address object
|
|
981
|
-
const formState = objectField_1.createObjectState(authorWithAddressConfig, {
|
|
1074
|
+
const formState = (0, objectField_1.createObjectState)(authorWithAddressConfig, {
|
|
982
1075
|
firstName: "f",
|
|
983
1076
|
// And nothing is in the address
|
|
984
1077
|
address: {},
|
|
@@ -989,7 +1082,7 @@ describe("formState", () => {
|
|
|
989
1082
|
});
|
|
990
1083
|
it("changedValue skips effectively empty nested fields", () => {
|
|
991
1084
|
// Given a new author with an address object
|
|
992
|
-
const formState = objectField_1.createObjectState(authorWithAddressConfig, {
|
|
1085
|
+
const formState = (0, objectField_1.createObjectState)(authorWithAddressConfig, {
|
|
993
1086
|
firstName: "f",
|
|
994
1087
|
// And only undefined keys are in the address
|
|
995
1088
|
address: { city: undefined },
|
|
@@ -1000,7 +1093,7 @@ describe("formState", () => {
|
|
|
1000
1093
|
});
|
|
1001
1094
|
it("can return only changed list fields", () => {
|
|
1002
1095
|
// Given an author with some books
|
|
1003
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, {
|
|
1096
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, {
|
|
1004
1097
|
id: "a:1",
|
|
1005
1098
|
firstName: "f",
|
|
1006
1099
|
books: [
|
|
@@ -1021,7 +1114,7 @@ describe("formState", () => {
|
|
|
1021
1114
|
});
|
|
1022
1115
|
it("can return only changed but incremental list fields", () => {
|
|
1023
1116
|
// Given an author
|
|
1024
|
-
const formState = objectField_1.createObjectState({
|
|
1117
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
1025
1118
|
id: { type: "value" },
|
|
1026
1119
|
// And the books collection is marked as incremental (i.e. line items)
|
|
1027
1120
|
books: {
|
|
@@ -1052,7 +1145,7 @@ describe("formState", () => {
|
|
|
1052
1145
|
});
|
|
1053
1146
|
it("uses the op key as a hint to use incremental list behavior", () => {
|
|
1054
1147
|
// Given an author
|
|
1055
|
-
const formState = objectField_1.createObjectState({
|
|
1148
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
1056
1149
|
id: { type: "value" },
|
|
1057
1150
|
// And the books collection is not explicitly marked as incremental
|
|
1058
1151
|
books: {
|
|
@@ -1085,7 +1178,7 @@ describe("formState", () => {
|
|
|
1085
1178
|
});
|
|
1086
1179
|
it("defaults the incremental op key to included when not set", () => {
|
|
1087
1180
|
// Given an author
|
|
1088
|
-
const formState = objectField_1.createObjectState({
|
|
1181
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
1089
1182
|
id: { type: "value" },
|
|
1090
1183
|
// And the books collection is not explicitly marked as incremental
|
|
1091
1184
|
books: {
|
|
@@ -1126,9 +1219,9 @@ describe("formState", () => {
|
|
|
1126
1219
|
});
|
|
1127
1220
|
});
|
|
1128
1221
|
it("can observe value changes", () => {
|
|
1129
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, { firstName: "f" });
|
|
1222
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, { firstName: "f" });
|
|
1130
1223
|
let ticks = 0;
|
|
1131
|
-
mobx_1.reaction(() => formState.value, () => ticks++, { equals: () => false });
|
|
1224
|
+
(0, mobx_1.reaction)(() => formState.value, () => ticks++, { equals: () => false });
|
|
1132
1225
|
expect(ticks).toEqual(0);
|
|
1133
1226
|
formState.firstName.value = "f";
|
|
1134
1227
|
expect(ticks).toEqual(0);
|
|
@@ -1136,9 +1229,9 @@ describe("formState", () => {
|
|
|
1136
1229
|
expect(ticks).toEqual(1);
|
|
1137
1230
|
});
|
|
1138
1231
|
it("can observe value changes on lists", () => {
|
|
1139
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, { books: [{ title: "b1" }] });
|
|
1232
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, { books: [{ title: "b1" }] });
|
|
1140
1233
|
let ticks = 0;
|
|
1141
|
-
mobx_1.reaction(() => formState.books.value, () => ticks++, { equals: () => false });
|
|
1234
|
+
(0, mobx_1.reaction)(() => formState.books.value, () => ticks++, { equals: () => false });
|
|
1142
1235
|
expect(ticks).toEqual(0);
|
|
1143
1236
|
formState.books.rows[0].title.value = "b1";
|
|
1144
1237
|
expect(ticks).toEqual(0);
|
|
@@ -1148,7 +1241,7 @@ describe("formState", () => {
|
|
|
1148
1241
|
expect(ticks).toEqual(2);
|
|
1149
1242
|
});
|
|
1150
1243
|
it("does not override a focused, changed field", () => {
|
|
1151
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1244
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1152
1245
|
// Given first name is focused
|
|
1153
1246
|
formState.firstName.focus();
|
|
1154
1247
|
// And they have wip edits
|
|
@@ -1163,7 +1256,7 @@ describe("formState", () => {
|
|
|
1163
1256
|
expect(formState.firstName.value).toEqual("fff");
|
|
1164
1257
|
});
|
|
1165
1258
|
it("does update a focused, unchanged field", () => {
|
|
1166
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1259
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1167
1260
|
// Given first name is focused, but has not changed
|
|
1168
1261
|
formState.firstName.focus();
|
|
1169
1262
|
// When a mutation result sets both firstName and lastName
|
|
@@ -1172,7 +1265,7 @@ describe("formState", () => {
|
|
|
1172
1265
|
expect(formState.firstName.value).toEqual("f2");
|
|
1173
1266
|
});
|
|
1174
1267
|
it("trims string values on blur", () => {
|
|
1175
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1268
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1176
1269
|
// Given the user is typing with spaces
|
|
1177
1270
|
formState.firstName.focus();
|
|
1178
1271
|
formState.firstName.set("f ");
|
|
@@ -1184,7 +1277,7 @@ describe("formState", () => {
|
|
|
1184
1277
|
expect(formState.firstName.value).toEqual("f");
|
|
1185
1278
|
});
|
|
1186
1279
|
it("trims string values on maybeAutoSave", () => {
|
|
1187
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1280
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1188
1281
|
// Given the user is typing with spaces
|
|
1189
1282
|
formState.firstName.focus();
|
|
1190
1283
|
formState.firstName.set("f ");
|
|
@@ -1198,7 +1291,7 @@ describe("formState", () => {
|
|
|
1198
1291
|
expect(formState.firstName.touched).toEqual(true);
|
|
1199
1292
|
});
|
|
1200
1293
|
it("trims empty values to undefined on blur", () => {
|
|
1201
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1294
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, { firstName: "f", lastName: "l" });
|
|
1202
1295
|
// Given the user is typing with only spaces
|
|
1203
1296
|
formState.firstName.focus();
|
|
1204
1297
|
formState.firstName.set(" ");
|
|
@@ -1216,7 +1309,7 @@ describe("formState", () => {
|
|
|
1216
1309
|
});
|
|
1217
1310
|
it("can work with both required inputs and optional fields", () => {
|
|
1218
1311
|
// And we drop it in a form.
|
|
1219
|
-
const form = objectField_1.createObjectState({
|
|
1312
|
+
const form = (0, objectField_1.createObjectState)({
|
|
1220
1313
|
id: { type: "value" },
|
|
1221
1314
|
firstName: { type: "value" },
|
|
1222
1315
|
}, { id: "a:1" });
|
|
@@ -1237,7 +1330,7 @@ describe("formState", () => {
|
|
|
1237
1330
|
});
|
|
1238
1331
|
it("can have child object states passed in as field states", () => {
|
|
1239
1332
|
// Given an author
|
|
1240
|
-
const a = objectField_1.createObjectState({
|
|
1333
|
+
const a = (0, objectField_1.createObjectState)({
|
|
1241
1334
|
id: { type: "value" },
|
|
1242
1335
|
// And address is modeled as a value object
|
|
1243
1336
|
address: { type: "value" },
|
|
@@ -1254,7 +1347,7 @@ describe("formState", () => {
|
|
|
1254
1347
|
});
|
|
1255
1348
|
it("provides isNewEntity", () => {
|
|
1256
1349
|
// Given an author without an id
|
|
1257
|
-
const formState = objectField_1.createObjectState(authorWithAddressConfig, {
|
|
1350
|
+
const formState = (0, objectField_1.createObjectState)(authorWithAddressConfig, {
|
|
1258
1351
|
firstName: "f",
|
|
1259
1352
|
address: { city: "c1", street: "s1" },
|
|
1260
1353
|
});
|
|
@@ -1269,7 +1362,7 @@ describe("formState", () => {
|
|
|
1269
1362
|
});
|
|
1270
1363
|
it("isNewEntity is false if there is no id field", () => {
|
|
1271
1364
|
// Given an author without an id field defined
|
|
1272
|
-
const formState = objectField_1.createObjectState({
|
|
1365
|
+
const formState = (0, objectField_1.createObjectState)({
|
|
1273
1366
|
firstName: { type: "value" },
|
|
1274
1367
|
address: { type: "object", config: { street: { type: "value" } } },
|
|
1275
1368
|
}, {});
|
|
@@ -1279,7 +1372,7 @@ describe("formState", () => {
|
|
|
1279
1372
|
});
|
|
1280
1373
|
it("provides isNewEntity for lists", () => {
|
|
1281
1374
|
// Given an author & books without an id
|
|
1282
|
-
const formState = objectField_1.createObjectState(authorWithBooksConfig, {
|
|
1375
|
+
const formState = (0, objectField_1.createObjectState)(authorWithBooksConfig, {
|
|
1283
1376
|
books: [{ title: "t1" }, { title: "t2" }],
|
|
1284
1377
|
});
|
|
1285
1378
|
// Then both books know they are new
|
|
@@ -1338,7 +1431,7 @@ describe("formState", () => {
|
|
|
1338
1431
|
});
|
|
1339
1432
|
it("can optionally ignore the order of list state rows when determining dirty state", () => {
|
|
1340
1433
|
// Given a list state in a specific order, and the `strictOrder` option set to `false`
|
|
1341
|
-
const a = objectField_1.createObjectState({
|
|
1434
|
+
const a = (0, objectField_1.createObjectState)({
|
|
1342
1435
|
books: {
|
|
1343
1436
|
type: "list",
|
|
1344
1437
|
strictOrder: false,
|
|
@@ -1357,14 +1450,14 @@ describe("formState", () => {
|
|
|
1357
1450
|
});
|
|
1358
1451
|
describe("fragments", () => {
|
|
1359
1452
|
it("ignores changes within fragments", () => {
|
|
1360
|
-
const a = objectField_1.createObjectState({ title: { type: "value" }, data: { type: "fragment" } }, { title: "b1", data: objectField_1.fragment({ foo: "1" }) });
|
|
1453
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value" }, data: { type: "fragment" } }, { title: "b1", data: (0, objectField_1.fragment)({ foo: "1" }) });
|
|
1361
1454
|
let numCalcs = 0;
|
|
1362
|
-
mobx_1.autorun(() => {
|
|
1455
|
+
(0, mobx_1.autorun)(() => {
|
|
1363
1456
|
numCalcs++;
|
|
1364
1457
|
noop(a.value);
|
|
1365
1458
|
});
|
|
1366
1459
|
expect(a.data.value).toEqual({ foo: "1" });
|
|
1367
|
-
expect(mobx_1.isObservable(a.data.value)).toBe(false);
|
|
1460
|
+
expect((0, mobx_1.isObservable)(a.data.value)).toBe(false);
|
|
1368
1461
|
expect(numCalcs).toEqual(1);
|
|
1369
1462
|
a.data.value = { foo: "2" };
|
|
1370
1463
|
expect(numCalcs).toEqual(2);
|
|
@@ -1372,19 +1465,19 @@ describe("formState", () => {
|
|
|
1372
1465
|
expect(numCalcs).toEqual(2);
|
|
1373
1466
|
});
|
|
1374
1467
|
it("ignores fragments in changedValue", () => {
|
|
1375
|
-
const a = objectField_1.createObjectState({ title: { type: "value" }, data: { type: "fragment" } }, { title: "b1", data: objectField_1.fragment({ foo: "1" }) });
|
|
1468
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value" }, data: { type: "fragment" } }, { title: "b1", data: (0, objectField_1.fragment)({ foo: "1" }) });
|
|
1376
1469
|
a.title.value = "b2";
|
|
1377
|
-
a.data.value = objectField_1.fragment({ foo: "2" });
|
|
1470
|
+
a.data.value = (0, objectField_1.fragment)({ foo: "2" });
|
|
1378
1471
|
expect(a.changedValue).toMatchInlineSnapshot(`
|
|
1379
|
-
|
|
1472
|
+
{
|
|
1380
1473
|
"title": "b2",
|
|
1381
1474
|
}
|
|
1382
1475
|
`);
|
|
1383
1476
|
});
|
|
1384
1477
|
it("ignores fragments in value", () => {
|
|
1385
|
-
const a = objectField_1.createObjectState({ title: { type: "value" }, data: { type: "fragment" } }, { title: "b1", data: objectField_1.fragment({ foo: "1" }) });
|
|
1478
|
+
const a = (0, objectField_1.createObjectState)({ title: { type: "value" }, data: { type: "fragment" } }, { title: "b1", data: (0, objectField_1.fragment)({ foo: "1" }) });
|
|
1386
1479
|
expect(a.value).toMatchInlineSnapshot(`
|
|
1387
|
-
|
|
1480
|
+
{
|
|
1388
1481
|
"title": "b1",
|
|
1389
1482
|
}
|
|
1390
1483
|
`);
|
|
@@ -1392,7 +1485,7 @@ describe("formState", () => {
|
|
|
1392
1485
|
});
|
|
1393
1486
|
it("sets id key correctly", () => {
|
|
1394
1487
|
// Given an author
|
|
1395
|
-
const a = objectField_1.createObjectState({
|
|
1488
|
+
const a = (0, objectField_1.createObjectState)({
|
|
1396
1489
|
id: { type: "value" },
|
|
1397
1490
|
otherId: { type: "value", isIdKey: true },
|
|
1398
1491
|
}, {});
|
|
@@ -1404,7 +1497,8 @@ class ObservableObject {
|
|
|
1404
1497
|
constructor() {
|
|
1405
1498
|
this.firstName = "first";
|
|
1406
1499
|
this.lastName = "last";
|
|
1407
|
-
|
|
1500
|
+
this.age = undefined;
|
|
1501
|
+
(0, mobx_1.makeAutoObservable)(this);
|
|
1408
1502
|
}
|
|
1409
1503
|
get fullName() {
|
|
1410
1504
|
return `${this.firstName} ${this.lastName}`;
|
|
@@ -1432,7 +1526,7 @@ const authorWithBooksConfig = {
|
|
|
1432
1526
|
},
|
|
1433
1527
|
};
|
|
1434
1528
|
function createAuthorInputState(input, maybeAutoSave) {
|
|
1435
|
-
return objectField_1.createObjectState(authorWithBooksConfig, input, { maybeAutoSave });
|
|
1529
|
+
return (0, objectField_1.createObjectState)(authorWithBooksConfig, input, { maybeAutoSave });
|
|
1436
1530
|
}
|
|
1437
1531
|
const authorWithAddressConfig = {
|
|
1438
1532
|
id: { type: "value" },
|
|
@@ -1468,6 +1562,6 @@ const authorWithAddressAndBooksConfig = {
|
|
|
1468
1562
|
},
|
|
1469
1563
|
};
|
|
1470
1564
|
function createAuthorWithAddressInputState(input) {
|
|
1471
|
-
return objectField_1.createObjectState(authorWithAddressConfig, input);
|
|
1565
|
+
return (0, objectField_1.createObjectState)(authorWithAddressConfig, input);
|
|
1472
1566
|
}
|
|
1473
1567
|
function noop(t) { }
|