@strictly/react-form 0.0.36 → 0.0.38
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/.out/core/mobx/FormModel.d.ts +3 -6
- package/.out/core/mobx/FormModel.js +2 -18
- package/.out/core/mobx/hooks.d.ts +1 -1
- package/.out/core/mobx/specs/FormModel.tests.js +28 -37
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/core/mobx/FormModel.ts +5 -20
- package/core/mobx/hooks.tsx +1 -1
- package/core/mobx/specs/FormModel.tests.ts +20 -57
- package/dist/index.cjs +2 -13
- package/dist/index.d.cts +5 -8
- package/dist/index.d.ts +5 -8
- package/dist/index.js +2 -13
- package/package.json +1 -1
|
@@ -31,16 +31,14 @@ export type ValuePathsToAdaptersOf<TypePathsToAdapters extends Partial<Readonly<
|
|
|
31
31
|
export type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string, FieldAdapter>>>> = UnionToIntersection<{
|
|
32
32
|
readonly [K in keyof TypePathsToAdapters]: TypePathsToAdapters[K] extends undefined ? undefined : unknown extends ContextOfFieldAdapter<NonNullable<TypePathsToAdapters[K]>> ? never : ContextOfFieldAdapter<NonNullable<TypePathsToAdapters[K]>>;
|
|
33
33
|
}[keyof TypePathsToAdapters] | {}>;
|
|
34
|
-
export type FormMode = 'edit' | 'create';
|
|
35
34
|
export type FormModelContextSource<ContextType, V, ValuePath extends string | number | symbol> = {
|
|
36
35
|
forPath(value: V, valuePath: ValuePath): ContextType;
|
|
37
36
|
};
|
|
38
|
-
export declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<FlattenedValuesOfType<T, '*'>, ContextType>, ContextType = ContextOf<TypePathsToAdapters>, ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>> {
|
|
37
|
+
export declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<FlattenedValuesOfType<T, '*'>, ContextType>, ContextType = ContextOf<TypePathsToAdapters>, ContextSource extends FormModelContextSource<ContextType, ValueOfType<ReadonlyTypeOfType<T>>, keyof ValuePathsToAdapters> = FormModelContextSource<ContextType, ValueOfType<ReadonlyTypeOfType<T>>, string | number | symbol>, ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>> {
|
|
39
38
|
readonly type: T;
|
|
40
39
|
private readonly originalValue;
|
|
41
40
|
protected readonly adapters: TypePathsToAdapters;
|
|
42
|
-
protected readonly contextSource:
|
|
43
|
-
protected readonly mode: FormMode;
|
|
41
|
+
protected readonly contextSource: ContextSource;
|
|
44
42
|
private accessor observableValue;
|
|
45
43
|
accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
|
|
46
44
|
accessor errorOverrides: FlattenedErrorOverrides<ValuePathsToAdapters>;
|
|
@@ -48,8 +46,7 @@ export declare abstract class FormModel<T extends Type, ValueToTypePaths extends
|
|
|
48
46
|
private readonly flattenedTypeDefs;
|
|
49
47
|
private readonly originalValues;
|
|
50
48
|
private readonly listIndicesToKeys;
|
|
51
|
-
constructor(type: T, originalValue: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters, contextSource:
|
|
52
|
-
get forceMutableFields(): boolean;
|
|
49
|
+
constructor(type: T, originalValue: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters, contextSource: ContextSource);
|
|
53
50
|
get value(): ValueOfType<ReadonlyTypeOfType<T>>;
|
|
54
51
|
get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
|
|
55
52
|
private get knownFields();
|
|
@@ -87,7 +87,7 @@ let FormModel = (() => {
|
|
|
87
87
|
set errorOverrides(value) { __classPrivateFieldSet(this, _FormModel_errorOverrides_accessor_storage, value, "f"); }
|
|
88
88
|
get validation() { return __classPrivateFieldGet(this, _FormModel_validation_accessor_storage, "f"); }
|
|
89
89
|
set validation(value) { __classPrivateFieldSet(this, _FormModel_validation_accessor_storage, value, "f"); }
|
|
90
|
-
constructor(type, originalValue, adapters, contextSource
|
|
90
|
+
constructor(type, originalValue, adapters, contextSource) {
|
|
91
91
|
Object.defineProperty(this, "type", {
|
|
92
92
|
enumerable: true,
|
|
93
93
|
configurable: true,
|
|
@@ -112,12 +112,6 @@ let FormModel = (() => {
|
|
|
112
112
|
writable: true,
|
|
113
113
|
value: contextSource
|
|
114
114
|
});
|
|
115
|
-
Object.defineProperty(this, "mode", {
|
|
116
|
-
enumerable: true,
|
|
117
|
-
configurable: true,
|
|
118
|
-
writable: true,
|
|
119
|
-
value: mode
|
|
120
|
-
});
|
|
121
115
|
_FormModel_observableValue_accessor_storage.set(this, __runInitializers(this, _observableValue_initializers, void 0));
|
|
122
116
|
_FormModel_fieldOverrides_accessor_storage.set(this, (__runInitializers(this, _observableValue_extraInitializers), __runInitializers(this, _fieldOverrides_initializers, void 0)));
|
|
123
117
|
_FormModel_errorOverrides_accessor_storage.set(this, (__runInitializers(this, _fieldOverrides_extraInitializers), __runInitializers(this, _errorOverrides_initializers, {})));
|
|
@@ -169,16 +163,6 @@ let FormModel = (() => {
|
|
|
169
163
|
return v && [v.value];
|
|
170
164
|
});
|
|
171
165
|
}
|
|
172
|
-
get forceMutableFields() {
|
|
173
|
-
switch (this.mode) {
|
|
174
|
-
case 'create':
|
|
175
|
-
return true;
|
|
176
|
-
case 'edit':
|
|
177
|
-
return false;
|
|
178
|
-
default:
|
|
179
|
-
throw new UnreachableError(this.mode);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
166
|
get value() {
|
|
183
167
|
// copy and strip out the mobx so this computed will fire every time anything changes
|
|
184
168
|
return copy(this.type, this.observableValue);
|
|
@@ -308,7 +292,7 @@ let FormModel = (() => {
|
|
|
308
292
|
return {
|
|
309
293
|
value: displayedValue,
|
|
310
294
|
error,
|
|
311
|
-
readonly
|
|
295
|
+
readonly,
|
|
312
296
|
required,
|
|
313
297
|
// make a copy of the index mapping and remove the final value (next id)
|
|
314
298
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReadonlyTypeOfType, type Type, type ValueOfType } from '@strictly/define';
|
|
2
2
|
import { type FormModel } from './FormModel';
|
|
3
|
-
type FormModelInterface<T extends Type = any> = Pick<FormModel<T, any, any, any, any>, 'fields' | 'value' | 'getValidation' | 'validateField' | 'setFieldValue' | 'validateSubmit' | 'isFieldDirty' | 'isValuePathActive'>;
|
|
3
|
+
type FormModelInterface<T extends Type = any> = Pick<FormModel<T, any, any, any, any, any>, 'fields' | 'value' | 'getValidation' | 'validateField' | 'setFieldValue' | 'validateSubmit' | 'isFieldDirty' | 'isValuePathActive'>;
|
|
4
4
|
type ValueOfModel<M extends FormModelInterface> = M extends FormModelInterface<infer T> ? ValueOfType<ReadonlyTypeOfType<T>> : never;
|
|
5
5
|
export declare function useDefaultMobxFormHooks<M extends FormModelInterface, F extends M['fields'] = M['fields']>(model: M, { onValidFieldSubmit, onValidFormSubmit, }?: {
|
|
6
6
|
onValidFieldSubmit?: <Path extends keyof F>(valuePath: Path) => void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expectDefinedAndReturn } from '@strictly/base';
|
|
2
|
-
import { booleanType,
|
|
2
|
+
import { booleanType, flattenValidatorsOfValidatingTypeWithMutability, list, nullType, numberType, object, record, stringType, union, } from '@strictly/define';
|
|
3
3
|
import { adapterFromTwoWayConverter, identityAdapter, } from 'core/mobx/fieldAdapterBuilder';
|
|
4
4
|
import { FormModel, Validation, } from 'core/mobx/FormModel';
|
|
5
5
|
import { mergeAdaptersWithValidators } from 'core/mobx/mergeFieldAdaptersWithValidators';
|
|
@@ -13,16 +13,25 @@ const IS_NAN_ERROR = 1;
|
|
|
13
13
|
const originalIntegerToStringAdapter = adapterFromTwoWayConverter(new IntegerToStringConverter(IS_NAN_ERROR), prototypingFieldValueFactory(0));
|
|
14
14
|
const originalBooleanToBooleanAdapter = identityAdapter(false);
|
|
15
15
|
class TestFormContextSource {
|
|
16
|
+
constructor(forceMutable) {
|
|
17
|
+
Object.defineProperty(this, "forceMutable", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: forceMutable
|
|
22
|
+
});
|
|
23
|
+
}
|
|
16
24
|
forPath(value, valuePath) {
|
|
17
25
|
return {
|
|
26
|
+
forceMutable: this.forceMutable,
|
|
18
27
|
value,
|
|
19
28
|
valuePath,
|
|
20
29
|
};
|
|
21
30
|
}
|
|
22
31
|
}
|
|
23
32
|
class TestFormModel extends FormModel {
|
|
24
|
-
constructor(type, originalValue, adapters,
|
|
25
|
-
super(type, originalValue, adapters, new TestFormContextSource()
|
|
33
|
+
constructor(type, originalValue, adapters, forceMutable = false) {
|
|
34
|
+
super(type, originalValue, adapters, new TestFormContextSource(forceMutable));
|
|
26
35
|
}
|
|
27
36
|
setFieldValueAndValidate(valuePath, value) {
|
|
28
37
|
this.setFieldValue(valuePath, value, Validation.Always);
|
|
@@ -81,7 +90,7 @@ describe('all', function () {
|
|
|
81
90
|
let model;
|
|
82
91
|
beforeEach(function () {
|
|
83
92
|
originalValue = 5;
|
|
84
|
-
model = new TestFormModel(typeDef, originalValue, adapters
|
|
93
|
+
model = new TestFormModel(typeDef, originalValue, adapters);
|
|
85
94
|
});
|
|
86
95
|
describe('accessors', function () {
|
|
87
96
|
it('gets the expected value', function () {
|
|
@@ -122,7 +131,7 @@ describe('all', function () {
|
|
|
122
131
|
readonly: false,
|
|
123
132
|
});
|
|
124
133
|
originalValue = 5;
|
|
125
|
-
model = new TestFormModel(typeDef, originalValue, adapters
|
|
134
|
+
model = new TestFormModel(typeDef, originalValue, adapters);
|
|
126
135
|
});
|
|
127
136
|
it('reports required status', function () {
|
|
128
137
|
expect(model.fields).toEqual(expect.objectContaining({
|
|
@@ -147,7 +156,7 @@ describe('all', function () {
|
|
|
147
156
|
4,
|
|
148
157
|
17,
|
|
149
158
|
];
|
|
150
|
-
model = new TestFormModel(typeDef, value, adapters
|
|
159
|
+
model = new TestFormModel(typeDef, value, adapters);
|
|
151
160
|
});
|
|
152
161
|
describe('accessors', function () {
|
|
153
162
|
it.each([
|
|
@@ -209,7 +218,7 @@ describe('all', function () {
|
|
|
209
218
|
a: 1,
|
|
210
219
|
b: 2,
|
|
211
220
|
};
|
|
212
|
-
model = new TestFormModel(typeDef, value, converters
|
|
221
|
+
model = new TestFormModel(typeDef, value, converters);
|
|
213
222
|
});
|
|
214
223
|
describe('accessors', function () {
|
|
215
224
|
it.each([
|
|
@@ -260,7 +269,7 @@ describe('all', function () {
|
|
|
260
269
|
a: 1,
|
|
261
270
|
b: true,
|
|
262
271
|
};
|
|
263
|
-
model = new TestFormModel(typeDef, value, converters
|
|
272
|
+
model = new TestFormModel(typeDef, value, converters);
|
|
264
273
|
});
|
|
265
274
|
describe('accessors', function () {
|
|
266
275
|
it.each([
|
|
@@ -306,7 +315,7 @@ describe('all', function () {
|
|
|
306
315
|
const originalValue = 2;
|
|
307
316
|
let model;
|
|
308
317
|
beforeEach(function () {
|
|
309
|
-
model = new TestFormModel(typeDef, originalValue, adapters
|
|
318
|
+
model = new TestFormModel(typeDef, originalValue, adapters);
|
|
310
319
|
});
|
|
311
320
|
describe('setFieldValueAndValidate', function () {
|
|
312
321
|
describe('success', function () {
|
|
@@ -408,7 +417,7 @@ describe('all', function () {
|
|
|
408
417
|
3,
|
|
409
418
|
7,
|
|
410
419
|
];
|
|
411
|
-
model = new TestFormModel(typeDef, originalValue, converters
|
|
420
|
+
model = new TestFormModel(typeDef, originalValue, converters);
|
|
412
421
|
});
|
|
413
422
|
describe('setFieldValueAndValidate', function () {
|
|
414
423
|
describe('success', function () {
|
|
@@ -518,6 +527,7 @@ describe('all', function () {
|
|
|
518
527
|
// if the value has since changed
|
|
519
528
|
value: model.value,
|
|
520
529
|
valuePath: '$.2',
|
|
530
|
+
forceMutable: false,
|
|
521
531
|
});
|
|
522
532
|
});
|
|
523
533
|
it('supplies the correct context value at the time it is being checked', function () {
|
|
@@ -529,6 +539,7 @@ describe('all', function () {
|
|
|
529
539
|
7,
|
|
530
540
|
],
|
|
531
541
|
valuePath: '$.2',
|
|
542
|
+
forceMutable: false,
|
|
532
543
|
});
|
|
533
544
|
});
|
|
534
545
|
});
|
|
@@ -710,7 +721,7 @@ describe('all', function () {
|
|
|
710
721
|
let model;
|
|
711
722
|
beforeEach(function () {
|
|
712
723
|
originalValue = null;
|
|
713
|
-
model = new TestFormModel(type, originalValue, adapters
|
|
724
|
+
model = new TestFormModel(type, originalValue, adapters);
|
|
714
725
|
});
|
|
715
726
|
it('has the expected fields', function () {
|
|
716
727
|
expect(model.fields).toEqual({
|
|
@@ -758,7 +769,7 @@ describe('all', function () {
|
|
|
758
769
|
const model = new TestFormModel(type, {
|
|
759
770
|
d: 'x',
|
|
760
771
|
a: 1,
|
|
761
|
-
}, adapters
|
|
772
|
+
}, adapters);
|
|
762
773
|
it.each([
|
|
763
774
|
[
|
|
764
775
|
'$',
|
|
@@ -781,7 +792,7 @@ describe('all', function () {
|
|
|
781
792
|
const model = new TestFormModel(type, {
|
|
782
793
|
d: 'y',
|
|
783
794
|
b: false,
|
|
784
|
-
}, adapters
|
|
795
|
+
}, adapters);
|
|
785
796
|
it.each([
|
|
786
797
|
[
|
|
787
798
|
'$',
|
|
@@ -813,7 +824,7 @@ describe('all', function () {
|
|
|
813
824
|
let model;
|
|
814
825
|
beforeEach(function () {
|
|
815
826
|
originalValue = 1;
|
|
816
|
-
model = new TestFormModel(typeDef, originalValue, converters
|
|
827
|
+
model = new TestFormModel(typeDef, originalValue, converters);
|
|
817
828
|
});
|
|
818
829
|
it('returns the default value for the fake field', function () {
|
|
819
830
|
expect(model.fields['$.fake']).toEqual(expect.objectContaining({
|
|
@@ -834,12 +845,12 @@ describe('all', function () {
|
|
|
834
845
|
});
|
|
835
846
|
});
|
|
836
847
|
});
|
|
837
|
-
describe('interaction with
|
|
848
|
+
describe('interaction with mutability', () => {
|
|
838
849
|
const typeDef = object().readonlyField('n', numberType.enforce(n => n < 10 ? 'err' : null));
|
|
839
850
|
const adapters = mergeAdaptersWithValidators({
|
|
840
851
|
$: identityAdapter({ n: 0 }),
|
|
841
852
|
'$.n': integerToStringAdapter,
|
|
842
|
-
},
|
|
853
|
+
}, flattenValidatorsOfValidatingTypeWithMutability(typeDef));
|
|
843
854
|
let originalValue;
|
|
844
855
|
beforeEach(() => {
|
|
845
856
|
originalValue = {
|
|
@@ -849,7 +860,7 @@ describe('all', function () {
|
|
|
849
860
|
describe('create mode', () => {
|
|
850
861
|
let model;
|
|
851
862
|
beforeEach(() => {
|
|
852
|
-
model = new TestFormModel(typeDef, originalValue, adapters,
|
|
863
|
+
model = new TestFormModel(typeDef, originalValue, adapters, true);
|
|
853
864
|
});
|
|
854
865
|
it('makes the field editable', () => {
|
|
855
866
|
expect(model.fields['$.n'].readonly).toBeFalsy();
|
|
@@ -862,26 +873,6 @@ describe('all', function () {
|
|
|
862
873
|
expect(model.validateAll()).toBeTruthy();
|
|
863
874
|
});
|
|
864
875
|
});
|
|
865
|
-
describe('edit model', () => {
|
|
866
|
-
let model;
|
|
867
|
-
beforeEach(function () {
|
|
868
|
-
model = new TestFormModel(typeDef, originalValue, adapters, 'edit');
|
|
869
|
-
});
|
|
870
|
-
it('respects the field being readonly', () => {
|
|
871
|
-
expect(model.fields['$.n'].readonly).toBeTruthy();
|
|
872
|
-
});
|
|
873
|
-
it('fails validation with invalid, clean data', () => {
|
|
874
|
-
expect(model.validateAll()).toBeFalsy();
|
|
875
|
-
});
|
|
876
|
-
it('fails validation with invalid, dirty data', () => {
|
|
877
|
-
model.setFieldValue('$.n', '2');
|
|
878
|
-
expect(model.validateAll()).toBeFalsy();
|
|
879
|
-
});
|
|
880
|
-
it('passes validation with valid, dirty data', () => {
|
|
881
|
-
model.setFieldValue('$.n', '10');
|
|
882
|
-
expect(model.validateAll()).toBeTruthy();
|
|
883
|
-
});
|
|
884
|
-
});
|
|
885
876
|
});
|
|
886
877
|
});
|
|
887
878
|
});
|