@strictly/react-form 0.0.12 → 0.0.14
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/field_adapter.d.ts +1 -0
- package/.out/core/mobx/form_model.d.ts +3 -1
- package/.out/core/mobx/form_model.js +23 -15
- package/.out/core/mobx/hooks.d.ts +2 -2
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +2 -2
- package/.out/core/mobx/specs/form_model.tests.js +26 -21
- package/.out/core/mobx/specs/sub_form_field_adapters.tests.js +14 -21
- package/.out/core/mobx/sub_form_field_adapters.d.ts +5 -6
- package/.out/core/mobx/sub_form_field_adapters.js +6 -11
- package/.out/core/mobx/types.d.ts +3 -3
- package/.out/index.d.ts +2 -0
- package/.out/index.js +2 -0
- package/.out/mantine/create_fields_view.js +3 -2
- package/.out/mantine/field_view.d.ts +18 -0
- package/.out/mantine/field_view.js +16 -0
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/util/empty.d.ts +1 -0
- package/.out/util/empty.js +3 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/core/mobx/field_adapter.ts +9 -0
- package/core/mobx/form_model.ts +26 -16
- package/core/mobx/hooks.tsx +2 -2
- package/core/mobx/merge_field_adapters_with_two_way_converter.ts +2 -1
- package/core/mobx/specs/form_model.tests.ts +35 -20
- package/core/mobx/specs/sub_form_field_adapters.tests.ts +14 -34
- package/core/mobx/sub_form_field_adapters.ts +11 -26
- package/core/mobx/types.ts +10 -7
- package/dist/index.cjs +94 -61
- package/dist/index.d.cts +32 -12
- package/dist/index.d.ts +32 -12
- package/dist/index.js +83 -51
- package/index.ts +2 -0
- package/mantine/create_fields_view.tsx +6 -2
- package/mantine/field_view.tsx +39 -0
- package/package.json +1 -1
- package/util/empty.tsx +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function Empty(): null;
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -7,12 +7,12 @@ $ tsup
|
|
|
7
7
|
[34mCLI[39m Target: es6
|
|
8
8
|
[34mCJS[39m Build start
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mCJS[39m [1mdist/index.cjs [22m[32m59.
|
|
11
|
-
[32mCJS[39m ⚡️ Build success in
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[32m55.
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m59.76 KB[39m
|
|
11
|
+
[32mCJS[39m ⚡️ Build success in 115ms
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m55.85 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 122ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
18
|
-
Done in
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 9788ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m37.25 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m37.25 KB[39m
|
|
18
|
+
Done in 10.94s.
|
|
@@ -38,3 +38,12 @@ export type ValuePathOfFieldAdapter<C extends FieldAdapter> = C extends FieldAda
|
|
|
38
38
|
infer ValuePath
|
|
39
39
|
> ? ValuePath
|
|
40
40
|
: never
|
|
41
|
+
|
|
42
|
+
export type ContextOfFieldAdapter<F extends FieldAdapter> = F extends FieldAdapter<
|
|
43
|
+
infer _From,
|
|
44
|
+
infer _To,
|
|
45
|
+
infer _E,
|
|
46
|
+
infer _P,
|
|
47
|
+
infer Context
|
|
48
|
+
> ? Context
|
|
49
|
+
: never
|
package/core/mobx/form_model.ts
CHANGED
|
@@ -102,13 +102,14 @@ export type ValuePathsToAdaptersOf<
|
|
|
102
102
|
}
|
|
103
103
|
: never
|
|
104
104
|
|
|
105
|
-
export class FormModel<
|
|
105
|
+
export abstract class FormModel<
|
|
106
106
|
T extends Type,
|
|
107
107
|
ValueToTypePaths extends Readonly<Record<string, string>>,
|
|
108
108
|
TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<
|
|
109
109
|
FlattenedValuesOfType<T, '*'>,
|
|
110
|
-
|
|
110
|
+
ContextType
|
|
111
111
|
>,
|
|
112
|
+
ContextType = {},
|
|
112
113
|
ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<
|
|
113
114
|
TypePathsToAdapters,
|
|
114
115
|
ValueToTypePaths
|
|
@@ -130,6 +131,7 @@ export class FormModel<
|
|
|
130
131
|
) {
|
|
131
132
|
this.value = mobxCopy(type, value)
|
|
132
133
|
this.flattenedTypeDefs = flattenTypesOfType(type)
|
|
134
|
+
const contextValue = this.toContext(value)
|
|
133
135
|
// pre-populate field overrides for consistent behavior when default information is overwritten
|
|
134
136
|
// then returned to
|
|
135
137
|
const conversions = flattenValueTo(
|
|
@@ -156,7 +158,8 @@ export class FormModel<
|
|
|
156
158
|
// no need to store a temporary value if the value cannot be written back
|
|
157
159
|
return
|
|
158
160
|
}
|
|
159
|
-
|
|
161
|
+
// cannot call this.context yet as the "this" pointer has not been fully created
|
|
162
|
+
return convert(value, valuePath, contextValue)
|
|
160
163
|
},
|
|
161
164
|
)
|
|
162
165
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -165,6 +168,13 @@ export class FormModel<
|
|
|
165
168
|
}) as FlattenedFieldOverrides<ValuePathsToAdapters>
|
|
166
169
|
}
|
|
167
170
|
|
|
171
|
+
@computed.struct
|
|
172
|
+
get context() {
|
|
173
|
+
return this.toContext(this.value)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>): ContextType
|
|
177
|
+
|
|
168
178
|
@computed
|
|
169
179
|
get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>> {
|
|
170
180
|
return new Proxy<SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>>(
|
|
@@ -248,14 +258,14 @@ export class FormModel<
|
|
|
248
258
|
? mobxCopy(
|
|
249
259
|
fieldTypeDef,
|
|
250
260
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
251
|
-
create(valuePath as string, this.
|
|
261
|
+
create(valuePath as string, this.context),
|
|
252
262
|
)
|
|
253
263
|
// fake values can't be copied
|
|
254
264
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
255
|
-
: create(valuePath as string, this.
|
|
265
|
+
: create(valuePath as string, this.context),
|
|
256
266
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
257
267
|
valuePath as string,
|
|
258
|
-
this.
|
|
268
|
+
this.context,
|
|
259
269
|
)
|
|
260
270
|
const error = this.errors[valuePath]
|
|
261
271
|
return {
|
|
@@ -341,7 +351,7 @@ export class FormModel<
|
|
|
341
351
|
: elementAdapter.create(
|
|
342
352
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
343
353
|
elementTypePath as string,
|
|
344
|
-
this.
|
|
354
|
+
this.context,
|
|
345
355
|
)
|
|
346
356
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
347
357
|
const originalList: any[] = accessor.value
|
|
@@ -486,7 +496,7 @@ export class FormModel<
|
|
|
486
496
|
assertExists(revert, 'setting value not supported {}', valuePath)
|
|
487
497
|
|
|
488
498
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
|
|
489
|
-
const conversion = revert(value, valuePath as any, this.
|
|
499
|
+
const conversion = revert(value, valuePath as any, this.context)
|
|
490
500
|
const accessor = this.getAccessorForValuePath(valuePath)
|
|
491
501
|
return runInAction(() => {
|
|
492
502
|
this.fieldOverrides[valuePath] = [value]
|
|
@@ -529,10 +539,10 @@ export class FormModel<
|
|
|
529
539
|
convert,
|
|
530
540
|
create,
|
|
531
541
|
} = adapter
|
|
532
|
-
const value = create(valuePath, this.
|
|
542
|
+
const value = create(valuePath, this.context)
|
|
533
543
|
const {
|
|
534
544
|
value: displayValue,
|
|
535
|
-
} = convert(value, valuePath, this.
|
|
545
|
+
} = convert(value, valuePath, this.context)
|
|
536
546
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
537
547
|
const key = valuePath as unknown as keyof ValuePathsToAdapters
|
|
538
548
|
runInAction(() => {
|
|
@@ -573,10 +583,10 @@ export class FormModel<
|
|
|
573
583
|
accessor != null
|
|
574
584
|
? accessor.value
|
|
575
585
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
576
|
-
: create(valuePath as string, this.
|
|
586
|
+
: create(valuePath as string, this.context),
|
|
577
587
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
578
588
|
valuePath as string,
|
|
579
|
-
this.
|
|
589
|
+
this.context,
|
|
580
590
|
)
|
|
581
591
|
const value = fieldOverride != null
|
|
582
592
|
? fieldOverride[0]
|
|
@@ -586,13 +596,13 @@ export class FormModel<
|
|
|
586
596
|
if (ignoreDefaultValue) {
|
|
587
597
|
const {
|
|
588
598
|
value: defaultDisplayValue,
|
|
589
|
-
} = convert(create(valuePath, this.
|
|
599
|
+
} = convert(create(valuePath, this.context), valuePath, this.context)
|
|
590
600
|
if (defaultDisplayValue === value) {
|
|
591
601
|
return true
|
|
592
602
|
}
|
|
593
603
|
}
|
|
594
604
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
595
|
-
const conversion = revert(value, valuePath as string, this.
|
|
605
|
+
const conversion = revert(value, valuePath as string, this.context)
|
|
596
606
|
return runInAction(() => {
|
|
597
607
|
switch (conversion.type) {
|
|
598
608
|
case UnreliableFieldConversionType.Failure:
|
|
@@ -645,14 +655,14 @@ export class FormModel<
|
|
|
645
655
|
const fieldOverride = this.fieldOverrides[adapterPath]
|
|
646
656
|
const {
|
|
647
657
|
value: storedValue,
|
|
648
|
-
} = convert(accessor.value, valuePath, this.
|
|
658
|
+
} = convert(accessor.value, valuePath, this.context)
|
|
649
659
|
const value = fieldOverride != null
|
|
650
660
|
? fieldOverride[0]
|
|
651
661
|
: storedValue
|
|
652
662
|
// TODO more nuanced comparison
|
|
653
663
|
const dirty = fieldOverride != null && fieldOverride[0] !== storedValue
|
|
654
664
|
|
|
655
|
-
const conversion = revert(value, valuePath, this.
|
|
665
|
+
const conversion = revert(value, valuePath, this.context)
|
|
656
666
|
switch (conversion.type) {
|
|
657
667
|
case UnreliableFieldConversionType.Failure:
|
|
658
668
|
this.errors[adapterPath] = conversion.error
|
package/core/mobx/hooks.tsx
CHANGED
|
@@ -13,13 +13,13 @@ import {
|
|
|
13
13
|
} from './types'
|
|
14
14
|
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
-
type ValueOfModel<M extends FormModel<any, any, any, any>> = M extends FormModel<infer T, any, any, any>
|
|
16
|
+
type ValueOfModel<M extends FormModel<any, any, any, any, any>> = M extends FormModel<infer T, any, any, any, any>
|
|
17
17
|
? ValueOfType<ReadonlyTypeOfType<T>>
|
|
18
18
|
: never
|
|
19
19
|
|
|
20
20
|
export function useDefaultMobxFormHooks<
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
-
M extends FormModel<any, any, any, any>,
|
|
22
|
+
M extends FormModel<any, any, any, any, any>,
|
|
23
23
|
F extends FormFieldsOfModel<M> = FormFieldsOfModel<M>,
|
|
24
24
|
>(
|
|
25
25
|
model: M,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from 'field_converters/chain_field_converter'
|
|
6
6
|
import { type TwoWayFieldConverter } from 'types/field_converters'
|
|
7
7
|
import {
|
|
8
|
+
type ContextOfFieldAdapter,
|
|
8
9
|
type ErrorOfFieldAdapter,
|
|
9
10
|
type FieldAdapter,
|
|
10
11
|
type FromOfFieldAdapter,
|
|
@@ -22,7 +23,7 @@ export type MergedOfFieldAdaptersWithTwoWayConverter<
|
|
|
22
23
|
ToOfFieldAdapter<FieldAdapters[K]>,
|
|
23
24
|
ErrorOfFieldAdapter<FieldAdapters[K]> | E,
|
|
24
25
|
ValuePathOfFieldAdapter<FieldAdapters[K]>,
|
|
25
|
-
Context
|
|
26
|
+
ContextOfFieldAdapter<FieldAdapters[K]> & Context
|
|
26
27
|
>
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
object,
|
|
9
9
|
record,
|
|
10
10
|
stringType,
|
|
11
|
+
type Type,
|
|
11
12
|
union,
|
|
12
13
|
type ValueOfType,
|
|
13
14
|
type ValueToTypePathsOfType,
|
|
@@ -45,6 +46,21 @@ const originalIntegerToStringAdapter = adapterFromTwoWayConverter(
|
|
|
45
46
|
|
|
46
47
|
const originalBooleanToBooleanAdapter = identityAdapter(false)
|
|
47
48
|
|
|
49
|
+
class TestFormModel<
|
|
50
|
+
T extends Type,
|
|
51
|
+
ValueToTypePaths extends Readonly<Record<string, string>>,
|
|
52
|
+
TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<
|
|
53
|
+
FlattenedValuesOfType<T, '*'>,
|
|
54
|
+
{}
|
|
55
|
+
>,
|
|
56
|
+
> extends FormModel<T, ValueToTypePaths, TypePathsToAdapters, {}> {
|
|
57
|
+
override toContext() {
|
|
58
|
+
return {
|
|
59
|
+
ctx: true,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
48
64
|
describe('all', function () {
|
|
49
65
|
const integerToStringAdapter = createMockedAdapter(
|
|
50
66
|
originalIntegerToStringAdapter,
|
|
@@ -171,7 +187,7 @@ describe('all', function () {
|
|
|
171
187
|
>
|
|
172
188
|
beforeEach(function () {
|
|
173
189
|
originalValue = 5
|
|
174
|
-
model = new
|
|
190
|
+
model = new TestFormModel<
|
|
175
191
|
typeof typeDef,
|
|
176
192
|
ValueToTypePathsOfType<typeof typeDef>,
|
|
177
193
|
typeof adapters
|
|
@@ -231,7 +247,7 @@ describe('all', function () {
|
|
|
231
247
|
readonly: false,
|
|
232
248
|
})
|
|
233
249
|
originalValue = 5
|
|
234
|
-
model = new
|
|
250
|
+
model = new TestFormModel<
|
|
235
251
|
typeof typeDef,
|
|
236
252
|
ValueToTypePathsOfType<typeof typeDef>,
|
|
237
253
|
typeof adapters
|
|
@@ -272,7 +288,7 @@ describe('all', function () {
|
|
|
272
288
|
4,
|
|
273
289
|
17,
|
|
274
290
|
]
|
|
275
|
-
model = new
|
|
291
|
+
model = new TestFormModel<
|
|
276
292
|
typeof typeDef,
|
|
277
293
|
ValueToTypePathsOfType<typeof typeDef>,
|
|
278
294
|
typeof adapters
|
|
@@ -350,7 +366,7 @@ describe('all', function () {
|
|
|
350
366
|
a: 1,
|
|
351
367
|
b: 2,
|
|
352
368
|
}
|
|
353
|
-
model = new
|
|
369
|
+
model = new TestFormModel<
|
|
354
370
|
typeof typeDef,
|
|
355
371
|
ValueToTypePathsOfType<typeof typeDef>,
|
|
356
372
|
typeof converters
|
|
@@ -420,7 +436,7 @@ describe('all', function () {
|
|
|
420
436
|
a: 1,
|
|
421
437
|
b: true,
|
|
422
438
|
}
|
|
423
|
-
model = new
|
|
439
|
+
model = new TestFormModel<
|
|
424
440
|
typeof typeDef,
|
|
425
441
|
ValueToTypePathsOfType<typeof typeDef>,
|
|
426
442
|
typeof converters
|
|
@@ -485,7 +501,7 @@ describe('all', function () {
|
|
|
485
501
|
typeof adapters
|
|
486
502
|
>
|
|
487
503
|
beforeEach(function () {
|
|
488
|
-
model = new
|
|
504
|
+
model = new TestFormModel<
|
|
489
505
|
typeof typeDef,
|
|
490
506
|
ValueToTypePathsOfType<typeof typeDef>,
|
|
491
507
|
typeof adapters
|
|
@@ -611,7 +627,7 @@ describe('all', function () {
|
|
|
611
627
|
3,
|
|
612
628
|
7,
|
|
613
629
|
]
|
|
614
|
-
model = new
|
|
630
|
+
model = new TestFormModel<
|
|
615
631
|
typeof typeDef,
|
|
616
632
|
ValueToTypePathsOfType<typeof typeDef>,
|
|
617
633
|
typeof converters
|
|
@@ -727,7 +743,7 @@ describe('all', function () {
|
|
|
727
743
|
let contextCopy: number[]
|
|
728
744
|
beforeEach(function () {
|
|
729
745
|
integerToStringAdapter.revert.mockImplementationOnce(function (_value, _path, context) {
|
|
730
|
-
contextCopy =
|
|
746
|
+
contextCopy = { ...context }
|
|
731
747
|
return {
|
|
732
748
|
type: UnreliableFieldConversionType.Success,
|
|
733
749
|
value: 1,
|
|
@@ -742,17 +758,16 @@ describe('all', function () {
|
|
|
742
758
|
expect(integerToStringAdapter.revert).toHaveBeenCalledWith(
|
|
743
759
|
'4',
|
|
744
760
|
'$.2',
|
|
745
|
-
|
|
746
|
-
|
|
761
|
+
{
|
|
762
|
+
ctx: true,
|
|
763
|
+
},
|
|
747
764
|
)
|
|
748
765
|
})
|
|
749
766
|
|
|
750
|
-
it('supplies the context
|
|
751
|
-
expect(contextCopy).toEqual(
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
7,
|
|
755
|
-
])
|
|
767
|
+
it('supplies the context', function () {
|
|
768
|
+
expect(contextCopy).toEqual({
|
|
769
|
+
ctx: true,
|
|
770
|
+
})
|
|
756
771
|
})
|
|
757
772
|
})
|
|
758
773
|
|
|
@@ -953,7 +968,7 @@ describe('all', function () {
|
|
|
953
968
|
>
|
|
954
969
|
beforeEach(function () {
|
|
955
970
|
originalValue = null
|
|
956
|
-
model = new
|
|
971
|
+
model = new TestFormModel<
|
|
957
972
|
typeof type,
|
|
958
973
|
ValueToTypePaths,
|
|
959
974
|
typeof adapters
|
|
@@ -1018,7 +1033,7 @@ describe('all', function () {
|
|
|
1018
1033
|
|
|
1019
1034
|
describe('isValuePathActive', function () {
|
|
1020
1035
|
describe('discriminator x', function () {
|
|
1021
|
-
const model = new
|
|
1036
|
+
const model = new TestFormModel<
|
|
1022
1037
|
typeof type,
|
|
1023
1038
|
ValueToTypePaths,
|
|
1024
1039
|
typeof adapters
|
|
@@ -1050,7 +1065,7 @@ describe('all', function () {
|
|
|
1050
1065
|
})
|
|
1051
1066
|
|
|
1052
1067
|
describe('discriminator y', function () {
|
|
1053
|
-
const model = new
|
|
1068
|
+
const model = new TestFormModel<
|
|
1054
1069
|
typeof type,
|
|
1055
1070
|
ValueToTypePaths,
|
|
1056
1071
|
typeof adapters
|
|
@@ -1102,7 +1117,7 @@ describe('all', function () {
|
|
|
1102
1117
|
>
|
|
1103
1118
|
beforeEach(function () {
|
|
1104
1119
|
originalValue = 1
|
|
1105
|
-
model = new
|
|
1120
|
+
model = new TestFormModel<
|
|
1106
1121
|
typeof typeDef,
|
|
1107
1122
|
JsonPaths,
|
|
1108
1123
|
typeof converters
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
list,
|
|
3
|
-
numberType,
|
|
4
|
-
object,
|
|
5
|
-
stringType,
|
|
6
|
-
} from '@strictly/define'
|
|
7
1
|
import { type FieldAdapter } from 'core/mobx/field_adapter'
|
|
8
2
|
import {
|
|
9
3
|
subFormFieldAdapters,
|
|
@@ -19,7 +13,6 @@ describe('subFormFieldAdapters', () => {
|
|
|
19
13
|
const adapters = subFormFieldAdapters(
|
|
20
14
|
{},
|
|
21
15
|
'$.a',
|
|
22
|
-
stringType,
|
|
23
16
|
)
|
|
24
17
|
|
|
25
18
|
it('equals expected type', () => {
|
|
@@ -35,19 +28,16 @@ describe('subFormFieldAdapters', () => {
|
|
|
35
28
|
const mockedFieldAdapter1 = mockDeep<Required<FieldAdapter<string, boolean, number, '$', string>>>()
|
|
36
29
|
const fieldAdapter1: FieldAdapter<string, boolean, number, '$', string> = mockedFieldAdapter1
|
|
37
30
|
|
|
38
|
-
const type = object().field('a', stringType)
|
|
39
31
|
const subAdapters = {
|
|
40
32
|
$: fieldAdapter1,
|
|
41
33
|
} as const
|
|
42
34
|
const adapters = subFormFieldAdapters<
|
|
43
35
|
typeof subAdapters,
|
|
44
36
|
'$.a',
|
|
45
|
-
{ '$.a': '$.a' }
|
|
46
|
-
typeof type
|
|
37
|
+
{ '$.a': '$.a' }
|
|
47
38
|
>(
|
|
48
39
|
subAdapters,
|
|
49
40
|
'$.a',
|
|
50
|
-
type,
|
|
51
41
|
)
|
|
52
42
|
|
|
53
43
|
beforeEach(() => {
|
|
@@ -58,9 +48,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
58
48
|
// TODO toEqualTypeOf (cannot reason about revert optionality, seems to be a TS issue as they
|
|
59
49
|
// are both optional AFAICT)
|
|
60
50
|
expectTypeOf(adapters).toMatchTypeOf<{
|
|
61
|
-
'$.a': FieldAdapter<string, boolean, number, '$.a',
|
|
62
|
-
a: string,
|
|
63
|
-
}>,
|
|
51
|
+
'$.a': FieldAdapter<string, boolean, number, '$.a', string>,
|
|
64
52
|
}>()
|
|
65
53
|
})
|
|
66
54
|
|
|
@@ -76,7 +64,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
76
64
|
}
|
|
77
65
|
mockedFieldAdapter1.convert.mockReturnValue(mockedReturnedValue)
|
|
78
66
|
|
|
79
|
-
const returnedValue = adapters['$.a'].convert('x', '$.a',
|
|
67
|
+
const returnedValue = adapters['$.a'].convert('x', '$.a', 'y')
|
|
80
68
|
expect(fieldAdapter1.convert).toHaveBeenCalledWith('x', '$', 'y')
|
|
81
69
|
expect(returnedValue).toEqual(mockedReturnedValue)
|
|
82
70
|
})
|
|
@@ -88,7 +76,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
88
76
|
} as const
|
|
89
77
|
mockedFieldAdapter1.revert.mockReturnValue(mockedReturnedValue)
|
|
90
78
|
|
|
91
|
-
const returnedValue = adapters['$.a'].revert?.(true, '$.a',
|
|
79
|
+
const returnedValue = adapters['$.a'].revert?.(true, '$.a', 'y')
|
|
92
80
|
expect(fieldAdapter1.revert).toHaveBeenCalledWith(true, '$', 'y')
|
|
93
81
|
expect(returnedValue).toEqual(mockedReturnedValue)
|
|
94
82
|
})
|
|
@@ -97,7 +85,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
97
85
|
const mockedReturnedValue = 'x'
|
|
98
86
|
mockedFieldAdapter1.create.mockReturnValue(mockedReturnedValue)
|
|
99
87
|
|
|
100
|
-
const returnedValue = adapters['$.a'].create('$.a',
|
|
88
|
+
const returnedValue = adapters['$.a'].create('$.a', 'y')
|
|
101
89
|
expect(fieldAdapter1.create).toHaveBeenCalledWith('$', 'y')
|
|
102
90
|
expect(returnedValue).toEqual(mockedReturnedValue)
|
|
103
91
|
})
|
|
@@ -109,15 +97,12 @@ describe('subFormFieldAdapters', () => {
|
|
|
109
97
|
const mockedFieldAdapter2 = mockDeep<FieldAdapter<number, boolean>>()
|
|
110
98
|
const fieldAdapter2: FieldAdapter<number, boolean> = mockedFieldAdapter2
|
|
111
99
|
|
|
112
|
-
const type = object()
|
|
113
|
-
.field('a', object().field('x', stringType).field('y', numberType))
|
|
114
100
|
const adapters = subFormFieldAdapters(
|
|
115
101
|
{
|
|
116
102
|
'$.x': fieldAdapter1,
|
|
117
103
|
'$.y': fieldAdapter2,
|
|
118
104
|
},
|
|
119
105
|
'$.a',
|
|
120
|
-
type,
|
|
121
106
|
)
|
|
122
107
|
|
|
123
108
|
beforeEach(() => {
|
|
@@ -141,12 +126,11 @@ describe('subFormFieldAdapters', () => {
|
|
|
141
126
|
})
|
|
142
127
|
|
|
143
128
|
describe('calls convert with correct paths and values', () => {
|
|
144
|
-
const subContext = {
|
|
145
|
-
x: 'a',
|
|
146
|
-
y: 1,
|
|
147
|
-
} as const
|
|
148
129
|
const context = {
|
|
149
|
-
a:
|
|
130
|
+
a: {
|
|
131
|
+
x: 'a',
|
|
132
|
+
y: 1,
|
|
133
|
+
},
|
|
150
134
|
}
|
|
151
135
|
|
|
152
136
|
it('calls $.a.x', () => {
|
|
@@ -158,7 +142,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
158
142
|
mockedFieldAdapter1.convert.mockReturnValue(mockedReturnedValue)
|
|
159
143
|
|
|
160
144
|
const returnedValue = adapters['$.a.x'].convert('b', '$.a.x', context)
|
|
161
|
-
expect(fieldAdapter1.convert).toHaveBeenCalledWith('b', '$.x',
|
|
145
|
+
expect(fieldAdapter1.convert).toHaveBeenCalledWith('b', '$.x', context)
|
|
162
146
|
expect(returnedValue).toEqual(mockedReturnedValue)
|
|
163
147
|
})
|
|
164
148
|
|
|
@@ -171,7 +155,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
171
155
|
mockedFieldAdapter2.convert.mockReturnValue(mockedReturnedValue)
|
|
172
156
|
|
|
173
157
|
const returnedValue = adapters['$.a.y'].convert(2, '$.a.y', context)
|
|
174
|
-
expect(fieldAdapter2.convert).toHaveBeenCalledWith(2, '$.y',
|
|
158
|
+
expect(fieldAdapter2.convert).toHaveBeenCalledWith(2, '$.y', context)
|
|
175
159
|
expect(returnedValue).toEqual(mockedReturnedValue)
|
|
176
160
|
})
|
|
177
161
|
})
|
|
@@ -180,7 +164,6 @@ describe('subFormFieldAdapters', () => {
|
|
|
180
164
|
describe('list adapter', () => {
|
|
181
165
|
const mockedFieldAdapter1 = mockDeep<Required<FieldAdapter<string, boolean, number, '$', string>>>()
|
|
182
166
|
const fieldAdapter1: FieldAdapter<string, boolean, number, '$', string> = mockedFieldAdapter1
|
|
183
|
-
const type = list(stringType)
|
|
184
167
|
const subAdapters = {
|
|
185
168
|
$: fieldAdapter1,
|
|
186
169
|
}
|
|
@@ -189,12 +172,10 @@ describe('subFormFieldAdapters', () => {
|
|
|
189
172
|
'$.*',
|
|
190
173
|
{
|
|
191
174
|
'$.*': `$.${number}`,
|
|
192
|
-
}
|
|
193
|
-
typeof type
|
|
175
|
+
}
|
|
194
176
|
>(
|
|
195
177
|
subAdapters,
|
|
196
178
|
'$.*',
|
|
197
|
-
type,
|
|
198
179
|
)
|
|
199
180
|
|
|
200
181
|
beforeEach(() => {
|
|
@@ -204,7 +185,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
204
185
|
it('equals expected type', () => {
|
|
205
186
|
// TODO toEqualTypeOf (seems to be a TS error)
|
|
206
187
|
expectTypeOf(adapters).toMatchTypeOf<{
|
|
207
|
-
'$.*': FieldAdapter<string, boolean, number, `$.${number}`, string
|
|
188
|
+
'$.*': FieldAdapter<string, boolean, number, `$.${number}`, string>,
|
|
208
189
|
}>()
|
|
209
190
|
})
|
|
210
191
|
|
|
@@ -216,7 +197,6 @@ describe('subFormFieldAdapters', () => {
|
|
|
216
197
|
|
|
217
198
|
describe('calls convert with correct paths and values', () => {
|
|
218
199
|
const subContext = 'a'
|
|
219
|
-
const context = [subContext]
|
|
220
200
|
|
|
221
201
|
it('calls $.*', () => {
|
|
222
202
|
const mockedReturnedValue = {
|
|
@@ -226,7 +206,7 @@ describe('subFormFieldAdapters', () => {
|
|
|
226
206
|
}
|
|
227
207
|
mockedFieldAdapter1.convert.mockReturnValue(mockedReturnedValue)
|
|
228
208
|
|
|
229
|
-
const returnedValue = adapters['$.*'].convert('b', '$.0',
|
|
209
|
+
const returnedValue = adapters['$.*'].convert('b', '$.0', subContext)
|
|
230
210
|
expect(fieldAdapter1.convert).toHaveBeenCalledWith('b', '$', subContext)
|
|
231
211
|
expect(returnedValue).toEqual(mockedReturnedValue)
|
|
232
212
|
})
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { type StringConcatOf } from '@strictly/base'
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
type ReadonlyTypeOfType,
|
|
5
|
-
type Type,
|
|
6
|
-
type ValueOfType,
|
|
7
|
-
} from '@strictly/define'
|
|
8
|
-
import {
|
|
3
|
+
type ContextOfFieldAdapter,
|
|
9
4
|
type ErrorOfFieldAdapter,
|
|
10
5
|
type FieldAdapter,
|
|
11
6
|
type FromOfFieldAdapter,
|
|
@@ -13,29 +8,27 @@ import {
|
|
|
13
8
|
type ValuePathOfFieldAdapter,
|
|
14
9
|
} from './field_adapter'
|
|
15
10
|
|
|
16
|
-
type SubFormFieldAdapter<F extends FieldAdapter, ValuePath extends string
|
|
11
|
+
type SubFormFieldAdapter<F extends FieldAdapter, ValuePath extends string> = FieldAdapter<
|
|
17
12
|
FromOfFieldAdapter<F>,
|
|
18
13
|
ToOfFieldAdapter<F>,
|
|
19
14
|
ErrorOfFieldAdapter<F>,
|
|
20
15
|
ValuePathOfFieldAdapter<F> extends StringConcatOf<'$', infer ValuePathSuffix> ? `${ValuePath}${ValuePathSuffix}`
|
|
21
16
|
// assume string (they don't care about the value path as a type) if there the path doesn't have a $ prefix
|
|
22
17
|
: string,
|
|
23
|
-
|
|
18
|
+
ContextOfFieldAdapter<F>
|
|
24
19
|
>
|
|
25
20
|
|
|
26
21
|
type SubFormFieldAdapters<
|
|
27
22
|
SubAdapters extends Record<string, FieldAdapter>,
|
|
28
23
|
TypePath extends string,
|
|
29
24
|
ValuePath extends string,
|
|
30
|
-
Context,
|
|
31
25
|
> = {
|
|
32
26
|
[
|
|
33
27
|
K in keyof SubAdapters as K extends StringConcatOf<'$', infer TypePathSuffix> ? `${TypePath}${TypePathSuffix}`
|
|
34
28
|
: never
|
|
35
29
|
]: SubFormFieldAdapter<
|
|
36
30
|
SubAdapters[K],
|
|
37
|
-
ValuePath
|
|
38
|
-
Context
|
|
31
|
+
ValuePath
|
|
39
32
|
>
|
|
40
33
|
}
|
|
41
34
|
|
|
@@ -43,27 +36,20 @@ export function subFormFieldAdapters<
|
|
|
43
36
|
SubAdapters extends Record<string, FieldAdapter>,
|
|
44
37
|
TypePath extends string,
|
|
45
38
|
TypePathsToValuePaths extends Record<TypePath, string>,
|
|
46
|
-
ContextType extends Type,
|
|
47
39
|
>(
|
|
48
40
|
subAdapters: SubAdapters,
|
|
49
41
|
parentTypePath: TypePath,
|
|
50
|
-
contextType: ContextType,
|
|
51
42
|
): SubFormFieldAdapters<
|
|
52
43
|
SubAdapters,
|
|
53
44
|
TypePath,
|
|
54
|
-
TypePathsToValuePaths[TypePath]
|
|
55
|
-
ValueOfType<ReadonlyTypeOfType<ContextType>>
|
|
45
|
+
TypePathsToValuePaths[TypePath]
|
|
56
46
|
> {
|
|
57
47
|
// assume the number of '.' in the type path will correspond to the number of '.' in the value path
|
|
58
48
|
const dotCount = parentTypePath.split('.').length
|
|
59
|
-
function
|
|
49
|
+
function getSubValuePath(valuePath: string) {
|
|
60
50
|
const parentValuePath = valuePath.split('.').slice(0, dotCount).join('.')
|
|
61
51
|
const subValuePath = valuePath.replace(parentValuePath, '$')
|
|
62
|
-
|
|
63
|
-
return [
|
|
64
|
-
subValuePath,
|
|
65
|
-
subContext,
|
|
66
|
-
] as const
|
|
52
|
+
return subValuePath
|
|
67
53
|
}
|
|
68
54
|
|
|
69
55
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -75,13 +61,13 @@ export function subFormFieldAdapters<
|
|
|
75
61
|
// adapt field adapter with new path and context
|
|
76
62
|
const adaptedAdapter: FieldAdapter = {
|
|
77
63
|
convert: (from, valuePath, context) => {
|
|
78
|
-
return subAdapter.convert(from,
|
|
64
|
+
return subAdapter.convert(from, getSubValuePath(valuePath), context)
|
|
79
65
|
},
|
|
80
66
|
create: (valuePath, context) => {
|
|
81
|
-
return subAdapter.create(
|
|
67
|
+
return subAdapter.create(getSubValuePath(valuePath), context)
|
|
82
68
|
},
|
|
83
69
|
revert: subAdapter.revert && ((from, valuePath, context) => {
|
|
84
|
-
return subAdapter.revert!(from,
|
|
70
|
+
return subAdapter.revert!(from, getSubValuePath(valuePath), context)
|
|
85
71
|
}),
|
|
86
72
|
}
|
|
87
73
|
acc[typePath] = adaptedAdapter
|
|
@@ -89,7 +75,6 @@ export function subFormFieldAdapters<
|
|
|
89
75
|
}, {}) as SubFormFieldAdapters<
|
|
90
76
|
SubAdapters,
|
|
91
77
|
TypePath,
|
|
92
|
-
TypePathsToValuePaths[TypePath]
|
|
93
|
-
ValueOfType<ReadonlyTypeOfType<ContextType>>
|
|
78
|
+
TypePathsToValuePaths[TypePath]
|
|
94
79
|
>
|
|
95
80
|
}
|