@strictly/react-form 0.0.11 → 0.0.13
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/{form_presenter.d.ts → form_model.d.ts} +15 -21
- package/.out/core/mobx/form_model.js +513 -0
- package/.out/core/mobx/hooks.d.ts +6 -25
- package/.out/core/mobx/hooks.js +14 -50
- package/.out/core/mobx/specs/{form_presenter.tests.js → form_model.tests.js} +46 -40
- package/.out/core/mobx/types.d.ts +4 -4
- package/.out/index.d.ts +1 -1
- package/.out/index.js +1 -1
- package/.out/mantine/create_fields_view.js +3 -2
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/core/mobx/{form_presenter.ts → form_model.ts} +287 -329
- package/core/mobx/hooks.tsx +26 -124
- package/core/mobx/specs/{form_presenter.tests.ts → form_model.tests.ts} +101 -94
- package/core/mobx/types.ts +12 -12
- package/dist/index.cjs +492 -534
- package/dist/index.d.cts +51 -73
- package/dist/index.d.ts +51 -73
- package/dist/index.js +488 -527
- package/index.ts +1 -1
- package/mantine/create_fields_view.tsx +6 -2
- package/mantine/hooks.tsx +2 -0
- package/package.json +1 -1
- package/.out/core/mobx/form_presenter.js +0 -525
- /package/.out/core/mobx/specs/{form_presenter.tests.d.ts → form_model.tests.d.ts} +0 -0
package/core/mobx/hooks.tsx
CHANGED
|
@@ -3,195 +3,97 @@ import {
|
|
|
3
3
|
type ValueOfType,
|
|
4
4
|
} from '@strictly/define'
|
|
5
5
|
import {
|
|
6
|
-
type FieldsViewProps,
|
|
7
|
-
} from 'core/props'
|
|
8
|
-
import {
|
|
9
|
-
type ComponentType,
|
|
10
6
|
useCallback,
|
|
11
|
-
useMemo,
|
|
12
7
|
} from 'react'
|
|
8
|
+
import { type FormModel } from './form_model'
|
|
13
9
|
import {
|
|
14
|
-
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
import { type FormPresenter } from './form_presenter'
|
|
18
|
-
import {
|
|
19
|
-
type FormFieldsOfPresenter,
|
|
20
|
-
type ToValueOfPresenterValuePath,
|
|
21
|
-
type ValuePathsOfPresenter,
|
|
10
|
+
type FormFieldsOfModel,
|
|
11
|
+
type ToValueOfModelValuePath,
|
|
12
|
+
type ValuePathsOfModel,
|
|
22
13
|
} from './types'
|
|
23
14
|
|
|
24
15
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
-
type
|
|
16
|
+
type ValueOfModel<M extends FormModel<any, any, any, any>> = M extends FormModel<infer T, any, any, any>
|
|
26
17
|
? ValueOfType<ReadonlyTypeOfType<T>>
|
|
27
18
|
: never
|
|
28
19
|
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
-
type ModelOfPresenter<P extends FormPresenter<any, any, any, any>> = ReturnType<P['createModel']>
|
|
31
|
-
|
|
32
|
-
export function useDefaultMobxFormHooks<
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
P extends FormPresenter<any, any, any, any>,
|
|
35
|
-
C extends ComponentType<FieldsViewProps<F>>,
|
|
36
|
-
F extends FormFieldsOfPresenter<P> = FormFieldsOfPresenter<P>,
|
|
37
|
-
>(
|
|
38
|
-
presenter: P,
|
|
39
|
-
value: ValueOfPresenter<P>,
|
|
40
|
-
options?: {
|
|
41
|
-
onValidFieldSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void,
|
|
42
|
-
onValidFormSubmit?: (model: ModelOfPresenter<P>, value: ValueOfPresenter<P>) => void,
|
|
43
|
-
},
|
|
44
|
-
): {
|
|
45
|
-
model: ModelOfPresenter<P>,
|
|
46
|
-
FormFields?: UnsafePartialComponent<C, FieldsViewProps<F>>,
|
|
47
|
-
onFormSubmit: () => void,
|
|
48
|
-
onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void,
|
|
49
|
-
onFieldFocus?(this: void, key: keyof F): void,
|
|
50
|
-
onFieldBlur?(this: void, key: keyof F): void,
|
|
51
|
-
onFieldSubmit?(this: void, key: keyof F): boolean | void,
|
|
52
|
-
}
|
|
53
20
|
export function useDefaultMobxFormHooks<
|
|
54
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
F extends FormFieldsOfPresenter<P> = FormFieldsOfPresenter<P>,
|
|
22
|
+
M extends FormModel<any, any, any, any>,
|
|
23
|
+
F extends FormFieldsOfModel<M> = FormFieldsOfModel<M>,
|
|
58
24
|
>(
|
|
59
|
-
|
|
60
|
-
value: ValueOfPresenter<P>,
|
|
61
|
-
options: {
|
|
62
|
-
onValidFieldSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void,
|
|
63
|
-
onValidFormSubmit?: (model: ModelOfPresenter<P>, value: ValueOfPresenter<P>) => void,
|
|
64
|
-
// TODO the types of C are not working, needs a local implementation to test
|
|
65
|
-
FormFieldsView: C,
|
|
66
|
-
},
|
|
67
|
-
): {
|
|
68
|
-
model: ModelOfPresenter<P>,
|
|
69
|
-
FormFields: UnsafePartialComponent<C, FieldsViewProps<F>>,
|
|
70
|
-
onFormSubmit: () => void,
|
|
71
|
-
onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void,
|
|
72
|
-
onFieldFocus?(this: void, key: keyof F): void,
|
|
73
|
-
onFieldBlur?(this: void, key: keyof F): void,
|
|
74
|
-
onFieldSubmit?(this: void, key: keyof F): boolean | void,
|
|
75
|
-
}
|
|
76
|
-
export function useDefaultMobxFormHooks<
|
|
77
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
-
P extends FormPresenter<any, any, any, any>,
|
|
79
|
-
C extends FieldsViewProps<F>,
|
|
80
|
-
F extends FormFieldsOfPresenter<P> = FormFieldsOfPresenter<P>,
|
|
81
|
-
>(
|
|
82
|
-
presenter: P,
|
|
83
|
-
value: ValueOfPresenter<P>,
|
|
25
|
+
model: M,
|
|
84
26
|
{
|
|
85
27
|
onValidFieldSubmit,
|
|
86
28
|
onValidFormSubmit,
|
|
87
|
-
FormFieldsView,
|
|
88
29
|
}: {
|
|
89
|
-
onValidFieldSubmit?: <Path extends
|
|
90
|
-
onValidFormSubmit?: (
|
|
91
|
-
FormFieldsView?: ComponentType<C>,
|
|
30
|
+
onValidFieldSubmit?: <Path extends ValuePathsOfModel<M>>(valuePath: Path) => void,
|
|
31
|
+
onValidFormSubmit?: (value: ValueOfModel<M>) => void,
|
|
92
32
|
} = {},
|
|
93
33
|
): {
|
|
94
|
-
model: ModelOfPresenter<P>,
|
|
95
|
-
FormFields?: UnsafePartialComponent<ComponentType<C>, FieldsViewProps<F>> | undefined,
|
|
96
34
|
onFormSubmit: () => void,
|
|
97
35
|
onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void,
|
|
98
36
|
onFieldFocus?(this: void, key: keyof F): void,
|
|
99
37
|
onFieldBlur?(this: void, key: keyof F): void,
|
|
100
38
|
onFieldSubmit?(this: void, key: keyof F): boolean | void,
|
|
101
39
|
} {
|
|
102
|
-
const model = useMemo(function () {
|
|
103
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
104
|
-
return presenter.createModel(value) as ReturnType<P['createModel']>
|
|
105
|
-
}, [
|
|
106
|
-
presenter,
|
|
107
|
-
value,
|
|
108
|
-
])
|
|
109
|
-
|
|
110
40
|
const onFieldValueChange = useCallback(
|
|
111
|
-
function<Path extends
|
|
41
|
+
function<Path extends ValuePathsOfModel<M>> (
|
|
112
42
|
path: Path,
|
|
113
|
-
value:
|
|
43
|
+
value: ToValueOfModelValuePath<M, Path>,
|
|
114
44
|
) {
|
|
115
|
-
|
|
116
|
-
|
|
45
|
+
// TODO do in one action
|
|
46
|
+
model.clearFieldError(path)
|
|
47
|
+
model.setFieldValue<Path>(path, value)
|
|
117
48
|
},
|
|
118
|
-
[
|
|
119
|
-
presenter,
|
|
120
|
-
model,
|
|
121
|
-
],
|
|
49
|
+
[model],
|
|
122
50
|
)
|
|
123
51
|
|
|
124
52
|
const onFieldSubmit = useCallback(
|
|
125
|
-
function<Path extends
|
|
126
|
-
if (
|
|
127
|
-
onValidFieldSubmit?.(
|
|
53
|
+
function<Path extends ValuePathsOfModel<M>> (valuePath: Path) {
|
|
54
|
+
if (model.validateField(valuePath)) {
|
|
55
|
+
onValidFieldSubmit?.(valuePath)
|
|
128
56
|
}
|
|
129
57
|
return false
|
|
130
58
|
},
|
|
131
59
|
[
|
|
132
|
-
presenter,
|
|
133
60
|
model,
|
|
134
61
|
onValidFieldSubmit,
|
|
135
62
|
],
|
|
136
63
|
)
|
|
137
64
|
|
|
138
65
|
const onFieldBlur = useCallback(
|
|
139
|
-
function<Path extends
|
|
66
|
+
function<Path extends ValuePathsOfModel<M>> (path: Path) {
|
|
140
67
|
// work around potential loss of focus prior to state potentially invalidating change triggering
|
|
141
68
|
// (e.g. changing a discriminator)
|
|
142
69
|
// TODO debounce?
|
|
143
70
|
setTimeout(function () {
|
|
144
|
-
if (
|
|
145
|
-
|
|
71
|
+
if (model.isValuePathActive(path)) {
|
|
72
|
+
model.validateField(path, true)
|
|
146
73
|
}
|
|
147
74
|
}, 100)
|
|
148
75
|
},
|
|
149
|
-
[
|
|
150
|
-
presenter,
|
|
151
|
-
model,
|
|
152
|
-
],
|
|
76
|
+
[model],
|
|
153
77
|
)
|
|
154
78
|
|
|
155
79
|
const onFormSubmit = useCallback(
|
|
156
80
|
function () {
|
|
157
|
-
if (
|
|
158
|
-
onValidFormSubmit?.(model
|
|
81
|
+
if (model.validateAll()) {
|
|
82
|
+
onValidFormSubmit?.(model.value)
|
|
159
83
|
}
|
|
160
84
|
},
|
|
161
85
|
[
|
|
162
|
-
presenter,
|
|
163
86
|
model,
|
|
164
87
|
onValidFormSubmit,
|
|
165
88
|
],
|
|
166
89
|
)
|
|
167
90
|
|
|
168
|
-
|
|
169
|
-
if (FormFieldsView == null) {
|
|
170
|
-
return undefined
|
|
171
|
-
}
|
|
172
|
-
return createUnsafePartialObserverComponent(FormFieldsView, (): FieldsViewProps<F> => {
|
|
173
|
-
return {
|
|
174
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
175
|
-
fields: model.fields as C['fields'],
|
|
176
|
-
onFieldBlur,
|
|
177
|
-
onFieldSubmit,
|
|
178
|
-
onFieldValueChange,
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
}, [
|
|
182
|
-
model,
|
|
183
|
-
FormFieldsView,
|
|
184
|
-
onFieldBlur,
|
|
185
|
-
onFieldSubmit,
|
|
186
|
-
onFieldValueChange,
|
|
187
|
-
])
|
|
91
|
+
// TODO have option to automatically bind all these callbacks to a FieldsView parameter
|
|
188
92
|
|
|
189
93
|
return {
|
|
190
|
-
model,
|
|
191
94
|
onFieldValueChange,
|
|
192
95
|
onFieldSubmit,
|
|
193
96
|
onFieldBlur,
|
|
194
97
|
onFormSubmit,
|
|
195
|
-
FormFields,
|
|
196
98
|
}
|
|
197
99
|
}
|
|
@@ -6,14 +6,10 @@ import {
|
|
|
6
6
|
nullType,
|
|
7
7
|
numberType,
|
|
8
8
|
object,
|
|
9
|
-
type ReadonlyOfTypeDef,
|
|
10
|
-
type ReadonlyTypeOfType,
|
|
11
9
|
record,
|
|
12
10
|
stringType,
|
|
13
|
-
type Type,
|
|
14
11
|
union,
|
|
15
12
|
type ValueOfType,
|
|
16
|
-
type ValueOfTypeDef,
|
|
17
13
|
type ValueToTypePathsOfType,
|
|
18
14
|
} from '@strictly/define'
|
|
19
15
|
import { type FieldAdapter } from 'core/mobx/field_adapter'
|
|
@@ -24,9 +20,8 @@ import {
|
|
|
24
20
|
import {
|
|
25
21
|
type FlattenedTypePathsToAdaptersOf,
|
|
26
22
|
FormModel,
|
|
27
|
-
FormPresenter,
|
|
28
23
|
type ValuePathsToAdaptersOf,
|
|
29
|
-
} from 'core/mobx/
|
|
24
|
+
} from 'core/mobx/form_model'
|
|
30
25
|
import { IntegerToStringConverter } from 'field_converters/integer_to_string_converter'
|
|
31
26
|
import { NullableToBooleanConverter } from 'field_converters/nullable_to_boolean_converter'
|
|
32
27
|
import { SelectDiscriminatedUnionConverter } from 'field_converters/select_value_type_converter'
|
|
@@ -43,21 +38,6 @@ import {
|
|
|
43
38
|
|
|
44
39
|
const IS_NAN_ERROR = 1
|
|
45
40
|
|
|
46
|
-
class TestFormPresenter<
|
|
47
|
-
T extends Type,
|
|
48
|
-
ValueToTypePaths extends Readonly<Record<string, string>>,
|
|
49
|
-
TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<
|
|
50
|
-
FlattenedValuesOfType<T, '*'>,
|
|
51
|
-
ValueOfType<ReadonlyTypeOfType<T>>
|
|
52
|
-
>,
|
|
53
|
-
> extends FormPresenter<T, ValueToTypePaths, TypePathsToAdapters> {
|
|
54
|
-
override createModel(value: ValueOfTypeDef<ReadonlyOfTypeDef<T['definition']>, {}>): FormModel<T, ValueToTypePaths,
|
|
55
|
-
TypePathsToAdapters, ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>>
|
|
56
|
-
{
|
|
57
|
-
return new FormModel(this.type, value, this.adapters)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
41
|
const originalIntegerToStringAdapter = adapterFromTwoWayConverter(
|
|
62
42
|
new IntegerToStringConverter(IS_NAN_ERROR),
|
|
63
43
|
prototypingFieldValueFactory(0),
|
|
@@ -492,20 +472,12 @@ describe('all', function () {
|
|
|
492
472
|
// TODO union
|
|
493
473
|
})
|
|
494
474
|
|
|
495
|
-
describe('
|
|
475
|
+
describe('FormModel', function () {
|
|
496
476
|
describe('literal', function () {
|
|
497
477
|
const typeDef = numberType
|
|
498
478
|
const adapters = {
|
|
499
479
|
$: integerToStringAdapter,
|
|
500
480
|
} as const
|
|
501
|
-
const presenter = new TestFormPresenter<
|
|
502
|
-
typeof typeDef,
|
|
503
|
-
ValueToTypePathsOfType<typeof typeDef>,
|
|
504
|
-
typeof adapters
|
|
505
|
-
>(
|
|
506
|
-
typeDef,
|
|
507
|
-
adapters,
|
|
508
|
-
)
|
|
509
481
|
const originalValue: ValueOfType<typeof typeDef> = 2
|
|
510
482
|
let model: FormModel<
|
|
511
483
|
typeof typeDef,
|
|
@@ -513,13 +485,21 @@ describe('all', function () {
|
|
|
513
485
|
typeof adapters
|
|
514
486
|
>
|
|
515
487
|
beforeEach(function () {
|
|
516
|
-
model =
|
|
488
|
+
model = new FormModel<
|
|
489
|
+
typeof typeDef,
|
|
490
|
+
ValueToTypePathsOfType<typeof typeDef>,
|
|
491
|
+
typeof adapters
|
|
492
|
+
>(
|
|
493
|
+
typeDef,
|
|
494
|
+
originalValue,
|
|
495
|
+
adapters,
|
|
496
|
+
)
|
|
517
497
|
})
|
|
518
498
|
|
|
519
499
|
describe('setFieldValueAndValidate', function () {
|
|
520
500
|
describe('success', function () {
|
|
521
501
|
beforeEach(function () {
|
|
522
|
-
|
|
502
|
+
model.setFieldValueAndValidate<'$'>('$', '1')
|
|
523
503
|
})
|
|
524
504
|
|
|
525
505
|
it('does set the underlying value', function () {
|
|
@@ -539,7 +519,7 @@ describe('all', function () {
|
|
|
539
519
|
describe('failure', function () {
|
|
540
520
|
describe('conversion fails', function () {
|
|
541
521
|
beforeEach(function () {
|
|
542
|
-
|
|
522
|
+
model.setFieldValueAndValidate<'$'>('$', 'x')
|
|
543
523
|
})
|
|
544
524
|
|
|
545
525
|
it('does not set the underlying value', function () {
|
|
@@ -565,7 +545,7 @@ describe('all', function () {
|
|
|
565
545
|
error: errorCode,
|
|
566
546
|
value: [newValue],
|
|
567
547
|
})
|
|
568
|
-
|
|
548
|
+
model.setFieldValueAndValidate<'$'>('$', '-1')
|
|
569
549
|
})
|
|
570
550
|
|
|
571
551
|
it('does set the underlying value', function () {
|
|
@@ -596,7 +576,7 @@ describe('all', function () {
|
|
|
596
576
|
],
|
|
597
577
|
] as const)('setFieldValue to %s', function (newValue, expectedValue) {
|
|
598
578
|
beforeEach(function () {
|
|
599
|
-
|
|
579
|
+
model.setFieldValue<'$'>('$', newValue)
|
|
600
580
|
})
|
|
601
581
|
|
|
602
582
|
it('does set the underlying value', function () {
|
|
@@ -619,14 +599,6 @@ describe('all', function () {
|
|
|
619
599
|
const converters = {
|
|
620
600
|
'$.*': integerToStringAdapter,
|
|
621
601
|
} as const
|
|
622
|
-
const presenter = new TestFormPresenter<
|
|
623
|
-
typeof typeDef,
|
|
624
|
-
ValueToTypePathsOfType<typeof typeDef>,
|
|
625
|
-
typeof converters
|
|
626
|
-
>(
|
|
627
|
-
typeDef,
|
|
628
|
-
converters,
|
|
629
|
-
)
|
|
630
602
|
let originalValue: ValueOfType<typeof typeDef>
|
|
631
603
|
let model: FormModel<
|
|
632
604
|
typeof typeDef,
|
|
@@ -639,13 +611,21 @@ describe('all', function () {
|
|
|
639
611
|
3,
|
|
640
612
|
7,
|
|
641
613
|
]
|
|
642
|
-
model =
|
|
614
|
+
model = new FormModel<
|
|
615
|
+
typeof typeDef,
|
|
616
|
+
ValueToTypePathsOfType<typeof typeDef>,
|
|
617
|
+
typeof converters
|
|
618
|
+
>(
|
|
619
|
+
typeDef,
|
|
620
|
+
originalValue,
|
|
621
|
+
converters,
|
|
622
|
+
)
|
|
643
623
|
})
|
|
644
624
|
|
|
645
625
|
describe('setFieldValueAndValidate', function () {
|
|
646
626
|
describe('success', function () {
|
|
647
627
|
beforeEach(function () {
|
|
648
|
-
|
|
628
|
+
model.setFieldValueAndValidate<'$.0'>('$.0', '100')
|
|
649
629
|
})
|
|
650
630
|
|
|
651
631
|
it('sets the underlying value', function () {
|
|
@@ -668,7 +648,7 @@ describe('all', function () {
|
|
|
668
648
|
|
|
669
649
|
describe('failure', function () {
|
|
670
650
|
beforeEach(function () {
|
|
671
|
-
|
|
651
|
+
model.setFieldValueAndValidate<'$.0'>('$.0', 'x')
|
|
672
652
|
})
|
|
673
653
|
|
|
674
654
|
it('does not set the underlying value', function () {
|
|
@@ -691,7 +671,7 @@ describe('all', function () {
|
|
|
691
671
|
'x',
|
|
692
672
|
])('setFieldValue to %s', function (newValue) {
|
|
693
673
|
beforeEach(function () {
|
|
694
|
-
|
|
674
|
+
model.setFieldValue('$.0', newValue)
|
|
695
675
|
})
|
|
696
676
|
|
|
697
677
|
it('does not set the underlying value', function () {
|
|
@@ -710,10 +690,10 @@ describe('all', function () {
|
|
|
710
690
|
|
|
711
691
|
describe('validate', function () {
|
|
712
692
|
beforeEach(function () {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
693
|
+
model.setFieldValue('$.0', 'x')
|
|
694
|
+
model.setFieldValue('$.1', '2')
|
|
695
|
+
model.setFieldValue('$.2', 'z')
|
|
696
|
+
model.validateAll()
|
|
717
697
|
})
|
|
718
698
|
|
|
719
699
|
it('contains errors for all invalid fields', function () {
|
|
@@ -756,7 +736,7 @@ describe('all', function () {
|
|
|
756
736
|
})
|
|
757
737
|
|
|
758
738
|
it('supplies the full, previous context when converting', function () {
|
|
759
|
-
|
|
739
|
+
model.setFieldValueAndValidate('$.2', '4')
|
|
760
740
|
|
|
761
741
|
expect(integerToStringAdapter.revert).toHaveBeenCalledOnce()
|
|
762
742
|
expect(integerToStringAdapter.revert).toHaveBeenCalledWith(
|
|
@@ -782,7 +762,7 @@ describe('all', function () {
|
|
|
782
762
|
model.errors['$.0'] = 0
|
|
783
763
|
model.errors['$.1'] = 1
|
|
784
764
|
model.errors['$.2'] = 2
|
|
785
|
-
|
|
765
|
+
model.addListItem('$', null, 0)
|
|
786
766
|
})
|
|
787
767
|
|
|
788
768
|
it('adds the list item to the underlying value', function () {
|
|
@@ -839,7 +819,7 @@ describe('all', function () {
|
|
|
839
819
|
|
|
840
820
|
describe('add defined value', function () {
|
|
841
821
|
beforeEach(function () {
|
|
842
|
-
|
|
822
|
+
model.addListItem('$', [5])
|
|
843
823
|
})
|
|
844
824
|
|
|
845
825
|
it('adds the expected value at the end', function () {
|
|
@@ -881,7 +861,7 @@ describe('all', function () {
|
|
|
881
861
|
|
|
882
862
|
describe('remove first item', function () {
|
|
883
863
|
beforeEach(function () {
|
|
884
|
-
|
|
864
|
+
model.removeListItem('$.0')
|
|
885
865
|
})
|
|
886
866
|
|
|
887
867
|
it('updates the underlying value', function () {
|
|
@@ -907,7 +887,7 @@ describe('all', function () {
|
|
|
907
887
|
|
|
908
888
|
describe('remove second item', function () {
|
|
909
889
|
beforeEach(function () {
|
|
910
|
-
|
|
890
|
+
model.removeListItem('$.1')
|
|
911
891
|
})
|
|
912
892
|
|
|
913
893
|
it('updates the underlying value', function () {
|
|
@@ -930,6 +910,25 @@ describe('all', function () {
|
|
|
930
910
|
})
|
|
931
911
|
})
|
|
932
912
|
})
|
|
913
|
+
|
|
914
|
+
describe('remove two items', function () {
|
|
915
|
+
beforeEach(function () {
|
|
916
|
+
model.removeListItem('$.0', '$.1')
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
it('updates the underlying value', function () {
|
|
920
|
+
expect(model.value).toEqual([7])
|
|
921
|
+
})
|
|
922
|
+
|
|
923
|
+
it('updates the field values and errors', function () {
|
|
924
|
+
expect(model.fields).toEqual({
|
|
925
|
+
'$.0': expect.objectContaining({
|
|
926
|
+
value: '7',
|
|
927
|
+
error: 2,
|
|
928
|
+
}),
|
|
929
|
+
})
|
|
930
|
+
})
|
|
931
|
+
})
|
|
933
932
|
})
|
|
934
933
|
})
|
|
935
934
|
|
|
@@ -946,14 +945,6 @@ describe('all', function () {
|
|
|
946
945
|
'$.*': integerToStringAdapter,
|
|
947
946
|
} as const
|
|
948
947
|
type ValueToTypePaths = ValueToTypePathsOfType<typeof type>
|
|
949
|
-
const presenter = new TestFormPresenter<
|
|
950
|
-
typeof type,
|
|
951
|
-
ValueToTypePaths,
|
|
952
|
-
typeof adapters
|
|
953
|
-
>(
|
|
954
|
-
type,
|
|
955
|
-
adapters,
|
|
956
|
-
)
|
|
957
948
|
let originalValue: ValueOfType<typeof type>
|
|
958
949
|
let model: FormModel<
|
|
959
950
|
typeof type,
|
|
@@ -962,7 +953,15 @@ describe('all', function () {
|
|
|
962
953
|
>
|
|
963
954
|
beforeEach(function () {
|
|
964
955
|
originalValue = null
|
|
965
|
-
model =
|
|
956
|
+
model = new FormModel<
|
|
957
|
+
typeof type,
|
|
958
|
+
ValueToTypePaths,
|
|
959
|
+
typeof adapters
|
|
960
|
+
>(
|
|
961
|
+
type,
|
|
962
|
+
originalValue,
|
|
963
|
+
adapters,
|
|
964
|
+
)
|
|
966
965
|
})
|
|
967
966
|
|
|
968
967
|
it('has the expected fields', function () {
|
|
@@ -979,7 +978,7 @@ describe('all', function () {
|
|
|
979
978
|
describe('setFieldValueAndValidate', function () {
|
|
980
979
|
describe('success', function () {
|
|
981
980
|
beforeEach(function () {
|
|
982
|
-
|
|
981
|
+
model.setFieldValueAndValidate<'$'>('$', true)
|
|
983
982
|
})
|
|
984
983
|
|
|
985
984
|
it('sets the underlying value', function () {
|
|
@@ -1016,21 +1015,21 @@ describe('all', function () {
|
|
|
1016
1015
|
'$.x:a': identityAdapter(0).narrow,
|
|
1017
1016
|
'$.y:b': identityAdapter(false).narrow,
|
|
1018
1017
|
} as const
|
|
1019
|
-
const presenter = new TestFormPresenter<
|
|
1020
|
-
typeof type,
|
|
1021
|
-
ValueToTypePaths,
|
|
1022
|
-
typeof adapters
|
|
1023
|
-
>(
|
|
1024
|
-
type,
|
|
1025
|
-
adapters,
|
|
1026
|
-
)
|
|
1027
1018
|
|
|
1028
1019
|
describe('isValuePathActive', function () {
|
|
1029
1020
|
describe('discriminator x', function () {
|
|
1030
|
-
const model =
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1021
|
+
const model = new FormModel<
|
|
1022
|
+
typeof type,
|
|
1023
|
+
ValueToTypePaths,
|
|
1024
|
+
typeof adapters
|
|
1025
|
+
>(
|
|
1026
|
+
type,
|
|
1027
|
+
{
|
|
1028
|
+
d: 'x',
|
|
1029
|
+
a: 1,
|
|
1030
|
+
},
|
|
1031
|
+
adapters,
|
|
1032
|
+
)
|
|
1034
1033
|
it.each([
|
|
1035
1034
|
[
|
|
1036
1035
|
'$',
|
|
@@ -1045,16 +1044,24 @@ describe('all', function () {
|
|
|
1045
1044
|
false,
|
|
1046
1045
|
],
|
|
1047
1046
|
] as const)('value path %s is active %s', function (path, expected) {
|
|
1048
|
-
const isValid =
|
|
1047
|
+
const isValid = model.isValuePathActive(path)
|
|
1049
1048
|
expect(isValid).toBe(expected)
|
|
1050
1049
|
})
|
|
1051
1050
|
})
|
|
1052
1051
|
|
|
1053
1052
|
describe('discriminator y', function () {
|
|
1054
|
-
const model =
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1053
|
+
const model = new FormModel<
|
|
1054
|
+
typeof type,
|
|
1055
|
+
ValueToTypePaths,
|
|
1056
|
+
typeof adapters
|
|
1057
|
+
>(
|
|
1058
|
+
type,
|
|
1059
|
+
{
|
|
1060
|
+
d: 'y',
|
|
1061
|
+
b: false,
|
|
1062
|
+
},
|
|
1063
|
+
adapters,
|
|
1064
|
+
)
|
|
1058
1065
|
it.each([
|
|
1059
1066
|
[
|
|
1060
1067
|
'$',
|
|
@@ -1069,7 +1076,7 @@ describe('all', function () {
|
|
|
1069
1076
|
true,
|
|
1070
1077
|
],
|
|
1071
1078
|
] as const)('value path %s is active %s', function (path, expected) {
|
|
1072
|
-
const isValid =
|
|
1079
|
+
const isValid = model.isValuePathActive(path)
|
|
1073
1080
|
expect(isValid).toBe(expected)
|
|
1074
1081
|
})
|
|
1075
1082
|
})
|
|
@@ -1087,14 +1094,6 @@ describe('all', function () {
|
|
|
1087
1094
|
$: '$',
|
|
1088
1095
|
'$.fake': '$.fake',
|
|
1089
1096
|
}
|
|
1090
|
-
const presenter = new TestFormPresenter<
|
|
1091
|
-
typeof typeDef,
|
|
1092
|
-
JsonPaths,
|
|
1093
|
-
typeof converters
|
|
1094
|
-
>(
|
|
1095
|
-
typeDef,
|
|
1096
|
-
converters,
|
|
1097
|
-
)
|
|
1098
1097
|
let originalValue: ValueOfType<typeof typeDef>
|
|
1099
1098
|
let model: FormModel<
|
|
1100
1099
|
typeof typeDef,
|
|
@@ -1103,7 +1102,15 @@ describe('all', function () {
|
|
|
1103
1102
|
>
|
|
1104
1103
|
beforeEach(function () {
|
|
1105
1104
|
originalValue = 1
|
|
1106
|
-
model =
|
|
1105
|
+
model = new FormModel<
|
|
1106
|
+
typeof typeDef,
|
|
1107
|
+
JsonPaths,
|
|
1108
|
+
typeof converters
|
|
1109
|
+
>(
|
|
1110
|
+
typeDef,
|
|
1111
|
+
originalValue,
|
|
1112
|
+
converters,
|
|
1113
|
+
)
|
|
1107
1114
|
})
|
|
1108
1115
|
|
|
1109
1116
|
it('returns the default value for the fake field', function () {
|
|
@@ -1114,7 +1121,7 @@ describe('all', function () {
|
|
|
1114
1121
|
|
|
1115
1122
|
describe('setting fake field', function () {
|
|
1116
1123
|
beforeEach(function () {
|
|
1117
|
-
|
|
1124
|
+
model.setFieldValue('$.fake', true)
|
|
1118
1125
|
})
|
|
1119
1126
|
|
|
1120
1127
|
it('stores the new value', function () {
|
package/core/mobx/types.ts
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
import { type ToOfFieldAdapter } from './field_adapter'
|
|
3
3
|
import {
|
|
4
4
|
type FlattenedConvertedFieldsOf,
|
|
5
|
-
type
|
|
6
|
-
} from './
|
|
5
|
+
type FormModel,
|
|
6
|
+
} from './form_model'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Used to extract the supported value paths from a presenter
|
|
10
10
|
*/
|
|
11
|
-
export type
|
|
11
|
+
export type ValuePathsOfModel<
|
|
12
12
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
-
Presenter extends
|
|
14
|
-
> = Presenter extends
|
|
13
|
+
Presenter extends FormModel<any, any, any, any>,
|
|
14
|
+
> = Presenter extends FormModel<
|
|
15
15
|
infer _1,
|
|
16
16
|
infer _2,
|
|
17
17
|
infer _3,
|
|
@@ -23,11 +23,11 @@ export type ValuePathsOfPresenter<
|
|
|
23
23
|
* Used to extract the render type (so the value that is passed to the view) of a given value path
|
|
24
24
|
* from a presenter
|
|
25
25
|
*/
|
|
26
|
-
export type
|
|
26
|
+
export type ToValueOfModelValuePath<
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
-
Presenter extends
|
|
29
|
-
K extends
|
|
30
|
-
> = Presenter extends
|
|
28
|
+
Presenter extends FormModel<any, any, any, any>,
|
|
29
|
+
K extends ValuePathsOfModel<Presenter>,
|
|
30
|
+
> = Presenter extends FormModel<
|
|
31
31
|
infer _1,
|
|
32
32
|
infer _2,
|
|
33
33
|
infer _3,
|
|
@@ -42,10 +42,10 @@ export type ToValueOfPresenterValuePath<
|
|
|
42
42
|
* is less typing, albeit at the cost of potentially getting type errors
|
|
43
43
|
* reported a long way away from the source
|
|
44
44
|
*/
|
|
45
|
-
export type
|
|
45
|
+
export type FormFieldsOfModel<
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
-
Presenter extends
|
|
48
|
-
> = Presenter extends
|
|
47
|
+
Presenter extends FormModel<any, any, any, any>,
|
|
48
|
+
> = Presenter extends FormModel<
|
|
49
49
|
infer _1,
|
|
50
50
|
infer _2,
|
|
51
51
|
infer _3,
|