@strictly/react-form 0.0.1 → 0.0.2

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.
Files changed (173) hide show
  1. package/.out/core/mobx/field_adapter.d.ts +7 -6
  2. package/.out/core/mobx/field_adapter_builder.d.ts +12 -13
  3. package/.out/core/mobx/field_adapter_builder.js +8 -12
  4. package/.out/core/mobx/field_adapters_of_values.d.ts +4 -0
  5. package/.out/core/mobx/flattened_adapters_of_fields.d.ts +2 -2
  6. package/.out/core/mobx/flattened_list_types_of_type.d.ts +8 -0
  7. package/.out/core/mobx/form_fields_of_field_adapters.d.ts +8 -0
  8. package/.out/core/mobx/form_presenter.d.ts +21 -24
  9. package/.out/core/mobx/form_presenter.js +64 -69
  10. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +13 -0
  11. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.js +11 -0
  12. package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +11 -0
  13. package/.out/core/mobx/merge_field_adapters_with_validators.js +45 -0
  14. package/.out/core/mobx/specs/fixtures.d.ts +7 -0
  15. package/.out/core/mobx/specs/fixtures.js +20 -0
  16. package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +5 -2
  17. package/.out/core/mobx/specs/{flattened_list_type_defs_of.tests.js → flattened_list_types_of_types.tests.js} +7 -7
  18. package/.out/core/mobx/specs/form_presenter.tests.js +162 -60
  19. package/.out/core/mobx/specs/merge_field_adapters_with_two_way_converter.js +89 -0
  20. package/.out/core/mobx/specs/merge_field_adapters_with_validators.tests.js +172 -0
  21. package/.out/core/mobx/types.d.ts +2 -2
  22. package/.out/field_converters/chain_field_converter.d.ts +3 -3
  23. package/.out/field_converters/chain_field_converter.js +17 -12
  24. package/.out/field_converters/identity_converter.d.ts +3 -3
  25. package/.out/field_converters/identity_converter.js +10 -6
  26. package/.out/field_converters/integer_to_string_converter.d.ts +5 -4
  27. package/.out/field_converters/integer_to_string_converter.js +13 -6
  28. package/.out/field_converters/list_converter.d.ts +2 -2
  29. package/.out/field_converters/list_converter.js +6 -1
  30. package/.out/field_converters/maybe_identity_converter.d.ts +3 -3
  31. package/.out/field_converters/maybe_identity_converter.js +3 -1
  32. package/.out/field_converters/nullable_to_boolean_converter.d.ts +9 -8
  33. package/.out/field_converters/nullable_to_boolean_converter.js +13 -7
  34. package/.out/field_converters/select_value_type_converter.d.ts +20 -15
  35. package/.out/field_converters/select_value_type_converter.js +29 -14
  36. package/.out/field_converters/specs/chain_field_converter.tests.d.ts +1 -0
  37. package/.out/field_converters/specs/chain_field_converter.tests.js +251 -0
  38. package/.out/field_converters/trimming_string_converter.d.ts +3 -3
  39. package/.out/field_converters/trimming_string_converter.js +7 -3
  40. package/.out/field_converters/validating_converter.d.ts +3 -3
  41. package/.out/field_converters/validating_converter.js +7 -5
  42. package/.out/index.d.ts +9 -2
  43. package/.out/index.js +9 -2
  44. package/.out/mantine/create_checkbox.d.ts +2 -3
  45. package/.out/mantine/create_checkbox.js +6 -5
  46. package/.out/mantine/create_pill.js +2 -2
  47. package/.out/mantine/create_radio.js +1 -1
  48. package/.out/mantine/create_radio_group.d.ts +2 -3
  49. package/.out/mantine/create_radio_group.js +4 -3
  50. package/.out/mantine/create_text_input.d.ts +2 -3
  51. package/.out/mantine/create_text_input.js +6 -5
  52. package/.out/mantine/create_value_input.d.ts +2 -3
  53. package/.out/mantine/create_value_input.js +6 -5
  54. package/.out/mantine/error_renderer.d.ts +6 -0
  55. package/.out/mantine/error_renderer.js +5 -0
  56. package/.out/mantine/hooks.d.ts +9 -13
  57. package/.out/mantine/hooks.js +10 -15
  58. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +7 -2
  59. package/.out/mantine/specs/checkbox_hooks.stories.js +33 -6
  60. package/.out/mantine/specs/list_hooks.stories.js +2 -2
  61. package/.out/mantine/specs/radio_group_hooks.stories.d.ts +7 -2
  62. package/.out/mantine/specs/radio_group_hooks.stories.js +33 -6
  63. package/.out/mantine/specs/select_hooks.stories.d.ts +8 -2
  64. package/.out/mantine/specs/select_hooks.stories.js +45 -8
  65. package/.out/mantine/specs/text_input_hooks.stories.d.ts +5 -1
  66. package/.out/mantine/specs/text_input_hooks.stories.js +23 -8
  67. package/.out/mantine/specs/value_input_hooks.stories.d.ts +7 -2
  68. package/.out/mantine/specs/value_input_hooks.stories.js +49 -15
  69. package/.out/mantine/types.d.ts +4 -1
  70. package/.out/tsconfig.tsbuildinfo +1 -1
  71. package/.out/types/error_of_field.d.ts +2 -0
  72. package/.out/types/error_of_field.js +1 -0
  73. package/.out/types/field.d.ts +1 -1
  74. package/.out/types/field_converters.d.ts +17 -10
  75. package/.out/types/field_converters.js +5 -5
  76. package/.out/types/flattened_validators_of_fields.d.ts +8 -0
  77. package/.out/types/flattened_validators_of_fields.js +1 -0
  78. package/.out/types/merge_validators.d.ts +7 -0
  79. package/.out/types/merge_validators.js +38 -0
  80. package/.out/types/specs/flattened_validators_of_fields.tests.d.ts +1 -0
  81. package/.out/types/specs/flattened_validators_of_fields.tests.js +16 -0
  82. package/.out/types/specs/merge_validators.tests.d.ts +1 -0
  83. package/.out/types/specs/merge_validators.tests.js +192 -0
  84. package/.out/util/partial.d.ts +11 -5
  85. package/.out/util/partial.js +55 -15
  86. package/.turbo/turbo-build.log +9 -9
  87. package/.turbo/turbo-check-types.log +1 -1
  88. package/.turbo/turbo-release$colon$exports.log +1 -1
  89. package/README.md +5 -1
  90. package/core/mobx/field_adapter.ts +15 -7
  91. package/core/mobx/field_adapter_builder.ts +39 -75
  92. package/core/mobx/field_adapters_of_values.ts +17 -0
  93. package/core/mobx/flattened_adapters_of_fields.ts +3 -3
  94. package/core/mobx/flattened_list_types_of_type.ts +17 -0
  95. package/core/mobx/form_fields_of_field_adapters.ts +16 -0
  96. package/core/mobx/form_presenter.ts +117 -104
  97. package/core/mobx/merge_field_adapters_with_two_way_converter.ts +68 -0
  98. package/core/mobx/merge_field_adapters_with_validators.ts +99 -0
  99. package/core/mobx/specs/fixtures.ts +73 -0
  100. package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +23 -2
  101. package/core/mobx/specs/flattened_list_types_of_types.tests.ts +35 -0
  102. package/core/mobx/specs/form_presenter.tests.ts +248 -124
  103. package/core/mobx/specs/merge_field_adapters_with_two_way_converter.ts +140 -0
  104. package/core/mobx/specs/merge_field_adapters_with_validators.tests.ts +259 -0
  105. package/core/mobx/types.ts +3 -3
  106. package/dist/index.cjs +459 -211
  107. package/dist/index.d.cts +153 -111
  108. package/dist/index.d.ts +153 -111
  109. package/dist/index.js +453 -200
  110. package/field_converters/chain_field_converter.ts +37 -23
  111. package/field_converters/identity_converter.ts +14 -10
  112. package/field_converters/integer_to_string_converter.ts +15 -9
  113. package/field_converters/list_converter.ts +8 -3
  114. package/field_converters/maybe_identity_converter.ts +7 -4
  115. package/field_converters/nullable_to_boolean_converter.ts +23 -16
  116. package/field_converters/select_value_type_converter.ts +86 -26
  117. package/field_converters/specs/chain_field_converter.tests.ts +302 -0
  118. package/field_converters/trimming_string_converter.ts +11 -6
  119. package/field_converters/validating_converter.ts +21 -11
  120. package/index.ts +9 -2
  121. package/mantine/create_checkbox.tsx +15 -8
  122. package/mantine/create_list.tsx +1 -4
  123. package/mantine/create_pill.tsx +2 -2
  124. package/mantine/create_radio.tsx +1 -1
  125. package/mantine/create_radio_group.tsx +8 -6
  126. package/mantine/create_text_input.tsx +20 -8
  127. package/mantine/create_value_input.tsx +17 -8
  128. package/mantine/error_renderer.ts +15 -0
  129. package/mantine/hooks.tsx +25 -51
  130. package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +126 -0
  131. package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +356 -0
  132. package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +208 -12
  133. package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +45 -0
  134. package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +194 -8
  135. package/mantine/specs/checkbox_hooks.stories.tsx +47 -7
  136. package/mantine/specs/list_hooks.stories.tsx +2 -2
  137. package/mantine/specs/radio_group_hooks.stories.tsx +47 -7
  138. package/mantine/specs/select_hooks.stories.tsx +55 -8
  139. package/mantine/specs/text_input_hooks.stories.tsx +32 -7
  140. package/mantine/specs/value_input_hooks.stories.tsx +57 -16
  141. package/mantine/types.ts +5 -1
  142. package/package.json +16 -4
  143. package/tsconfig.json +1 -0
  144. package/types/error_of_field.ts +3 -0
  145. package/types/field.ts +1 -1
  146. package/types/field_converters.ts +21 -10
  147. package/types/flattened_validators_of_fields.ts +34 -0
  148. package/types/merge_validators.ts +80 -0
  149. package/types/specs/error_type_of_field.tests.ts +2 -2
  150. package/types/specs/flattened_validators_of_fields.tests.ts +93 -0
  151. package/types/specs/merge_validators.tests.ts +267 -0
  152. package/util/partial.tsx +200 -16
  153. package/.out/core/mobx/flattened_list_type_defs_of.d.ts +0 -8
  154. package/.out/field_validators/minimum_string_length_field_validator.d.ts +0 -2
  155. package/.out/field_validators/minimum_string_length_field_validator.js +0 -8
  156. package/.out/types/error_type_of_field.d.ts +0 -2
  157. package/.out/types/field_validator.d.ts +0 -3
  158. package/.out/types/flattened_form_fields_of.d.ts +0 -9
  159. package/.out/types/specs/flattened_form_fields_of.tests.js +0 -13
  160. package/core/mobx/flattened_list_type_defs_of.ts +0 -17
  161. package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +0 -35
  162. package/field_validators/minimum_string_length_field_validator.ts +0 -13
  163. package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +0 -227
  164. package/types/error_type_of_field.ts +0 -3
  165. package/types/field_validator.ts +0 -7
  166. package/types/flattened_form_fields_of.ts +0 -16
  167. package/types/specs/flattened_form_fields_of.tests.ts +0 -43
  168. /package/.out/core/mobx/{flattened_list_type_defs_of.js → field_adapters_of_values.js} +0 -0
  169. /package/.out/core/mobx/{specs/flattened_list_type_defs_of.tests.d.ts → flattened_list_types_of_type.js} +0 -0
  170. /package/.out/{types/error_type_of_field.js → core/mobx/form_fields_of_field_adapters.js} +0 -0
  171. /package/.out/{types/field_validator.js → core/mobx/specs/flattened_list_types_of_types.tests.d.ts} +0 -0
  172. /package/.out/{types/flattened_form_fields_of.js → core/mobx/specs/merge_field_adapters_with_two_way_converter.d.ts} +0 -0
  173. /package/.out/{types/specs/flattened_form_fields_of.tests.d.ts → core/mobx/specs/merge_field_adapters_with_validators.tests.d.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { expectDefinedAndReturn } from '@strictly/base'
2
2
  import {
3
3
  booleanType,
4
- type FlattenedValueTypesOf,
4
+ type FlattenedValuesOfType,
5
5
  list,
6
6
  nullType,
7
7
  numberType,
@@ -9,8 +9,8 @@ import {
9
9
  record,
10
10
  stringType,
11
11
  union,
12
- type ValueToTypePathsOf,
13
- type ValueTypeOf,
12
+ type ValueOfType,
13
+ type ValueToTypePathsOfType,
14
14
  } from '@strictly/define'
15
15
  import { type FieldAdapter } from 'core/mobx/field_adapter'
16
16
  import {
@@ -25,56 +25,38 @@ import {
25
25
  } from 'core/mobx/form_presenter'
26
26
  import { IntegerToStringConverter } from 'field_converters/integer_to_string_converter'
27
27
  import { NullableToBooleanConverter } from 'field_converters/nullable_to_boolean_converter'
28
+ import { SelectDiscriminatedUnionConverter } from 'field_converters/select_value_type_converter'
28
29
  import { prototypingFieldValueFactory } from 'field_value_factories/prototyping_field_value_factory'
29
30
  import { type Simplify } from 'type-fest'
30
31
  import { type Field } from 'types/field'
31
32
  import {
32
- FieldConversionResult,
33
+ UnreliableFieldConversionType,
33
34
  } from 'types/field_converters'
34
- import { type Mocked } from 'vitest'
35
35
  import {
36
- mock,
37
- mockClear,
38
- } from 'vitest-mock-extended'
36
+ createMockedAdapter,
37
+ resetMockAdapter,
38
+ } from './fixtures'
39
39
 
40
40
  const IS_NAN_ERROR = 1
41
41
 
42
- function createMockedAdapter<
43
- E,
44
- To,
45
- From,
46
- ValuePath extends string,
47
- >({
48
- convert,
49
- revert,
50
- create,
51
- }: FieldAdapter<From, To, E, ValuePath>): Mocked<
52
- Required<FieldAdapter<From, To, E, ValuePath>>
53
- > {
54
- const mockedAdapter = mock<Required<FieldAdapter<From, To, E, ValuePath>>>()
55
- if (revert) {
56
- mockedAdapter.revert?.mockImplementation(revert)
57
- }
58
- mockedAdapter.convert.mockImplementation(convert)
59
- mockedAdapter.create.mockImplementation(create)
60
-
61
- return mockedAdapter
62
- }
42
+ const originalIntegerToStringAdapter = adapterFromTwoWayConverter(
43
+ new IntegerToStringConverter(IS_NAN_ERROR),
44
+ prototypingFieldValueFactory(0),
45
+ )
46
+
47
+ const originalBooleanToBooleanAdapter = identityAdapter(false)
63
48
 
64
49
  describe('all', function () {
65
50
  const integerToStringAdapter = createMockedAdapter(
66
- adapterFromTwoWayConverter(
67
- new IntegerToStringConverter(IS_NAN_ERROR),
68
- prototypingFieldValueFactory(0),
69
- ),
51
+ originalIntegerToStringAdapter,
70
52
  )
71
53
  const booleanToBooleanAdapter = createMockedAdapter(
72
- identityAdapter(false),
54
+ originalBooleanToBooleanAdapter,
73
55
  )
74
56
 
75
57
  beforeEach(function () {
76
- mockClear(integerToStringAdapter)
77
- mockClear(booleanToBooleanAdapter)
58
+ resetMockAdapter(originalIntegerToStringAdapter, integerToStringAdapter)
59
+ resetMockAdapter(originalBooleanToBooleanAdapter, booleanToBooleanAdapter)
78
60
  })
79
61
 
80
62
  describe('FlattenedTypePathsToConvertersOf', function () {
@@ -99,14 +81,14 @@ describe('all', function () {
99
81
  const typeDef = record<typeof numberType, 'a' | 'b'>(numberType)
100
82
  type T = Simplify<
101
83
  FlattenedTypePathsToAdaptersOf<
102
- FlattenedValueTypesOf<typeof typeDef>,
103
- ValueTypeOf<typeof typeDef>
84
+ FlattenedValuesOfType<typeof typeDef>,
85
+ ValueOfType<typeof typeDef>
104
86
  >
105
87
  >
106
88
  let t: Partial<{
107
- readonly $: ConvenientFieldAdapter<Readonly<Record<'a' | 'b', number>>, ValueTypeOf<typeof typeDef>>,
108
- readonly ['$.a']: ConvenientFieldAdapter<number, ValueTypeOf<typeof typeDef>>,
109
- readonly ['$.b']: ConvenientFieldAdapter<number, ValueTypeOf<typeof typeDef>>,
89
+ readonly $: ConvenientFieldAdapter<Readonly<Record<'a' | 'b', number>>, ValueOfType<typeof typeDef>>,
90
+ readonly ['$.a']: ConvenientFieldAdapter<number, ValueOfType<typeof typeDef>>,
91
+ readonly ['$.b']: ConvenientFieldAdapter<number, ValueOfType<typeof typeDef>>,
110
92
  }>
111
93
 
112
94
  it('equals expected type', function () {
@@ -116,16 +98,16 @@ describe('all', function () {
116
98
 
117
99
  describe('object', function () {
118
100
  const typeDef = object()
119
- .set('x', stringType)
120
- .set('y', booleanType)
101
+ .field('x', stringType)
102
+ .field('y', booleanType)
121
103
  type T = FlattenedTypePathsToAdaptersOf<
122
- FlattenedValueTypesOf<typeof typeDef>,
123
- ValueTypeOf<typeof typeDef>
104
+ FlattenedValuesOfType<typeof typeDef>,
105
+ ValueOfType<typeof typeDef>
124
106
  >
125
107
  let t: Partial<{
126
- readonly $: ConvenientFieldAdapter<{ readonly x: string, readonly y: boolean }, ValueTypeOf<typeof typeDef>>,
127
- readonly ['$.x']: ConvenientFieldAdapter<string, ValueTypeOf<typeof typeDef>>,
128
- readonly ['$.y']: ConvenientFieldAdapter<boolean, ValueTypeOf<typeof typeDef>>,
108
+ readonly $: ConvenientFieldAdapter<{ readonly x: string, readonly y: boolean }, ValueOfType<typeof typeDef>>,
109
+ readonly ['$.x']: ConvenientFieldAdapter<string, ValueOfType<typeof typeDef>>,
110
+ readonly ['$.y']: ConvenientFieldAdapter<boolean, ValueOfType<typeof typeDef>>,
129
111
  }>
130
112
  it('equals expected type', function () {
131
113
  expectTypeOf(t).toEqualTypeOf<T>()
@@ -177,57 +159,100 @@ describe('all', function () {
177
159
 
178
160
  describe('FormModel', function () {
179
161
  describe('literal', function () {
180
- const typeDef = numberType
181
- const adapters = {
182
- $: integerToStringAdapter,
183
- } as const
184
- let originalValue: ValueTypeOf<typeof typeDef>
185
- let model: FormModel<
186
- typeof typeDef,
187
- ValueToTypePathsOf<typeof typeDef>,
188
- typeof adapters
189
- >
190
- beforeEach(function () {
191
- originalValue = 5
192
- model = new FormModel<
162
+ describe('optional', function () {
163
+ const typeDef = numberType
164
+ const adapters = {
165
+ $: integerToStringAdapter,
166
+ } as const
167
+ let originalValue: ValueOfType<typeof typeDef>
168
+ let model: FormModel<
193
169
  typeof typeDef,
194
- ValueToTypePathsOf<typeof typeDef>,
170
+ ValueToTypePathsOfType<typeof typeDef>,
195
171
  typeof adapters
196
- >(
197
- typeDef,
198
- originalValue,
199
- adapters,
200
- )
201
- })
172
+ >
173
+ beforeEach(function () {
174
+ originalValue = 5
175
+ model = new FormModel<
176
+ typeof typeDef,
177
+ ValueToTypePathsOfType<typeof typeDef>,
178
+ typeof adapters
179
+ >(
180
+ typeDef,
181
+ originalValue,
182
+ adapters,
183
+ )
184
+ })
202
185
 
203
- describe('accessors', function () {
204
- it('gets the expected value', function () {
205
- const accessor = expectDefinedAndReturn(model.accessors.$)
206
- expect(accessor.value).toEqual(originalValue)
186
+ describe('accessors', function () {
187
+ it('gets the expected value', function () {
188
+ const accessor = expectDefinedAndReturn(model.accessors.$)
189
+ expect(accessor.value).toEqual(originalValue)
190
+ })
191
+
192
+ it('sets the underlying value', function () {
193
+ const newValue = 1
194
+ const accessor = expectDefinedAndReturn(model.accessors.$)
195
+ accessor.set(newValue)
196
+ expect(model.value).toEqual(newValue)
197
+ })
207
198
  })
208
199
 
209
- it('sets the underlying value', function () {
210
- const newValue = 1
211
- const accessor = expectDefinedAndReturn(model.accessors.$)
212
- accessor.set(newValue)
213
- expect(model.value).toEqual(newValue)
200
+ describe('fields', function () {
201
+ it('equals expected value', function () {
202
+ expect(model.fields).toEqual(
203
+ expect.objectContaining({
204
+ $: expect.objectContaining({
205
+ value: '5',
206
+ }),
207
+ }),
208
+ )
209
+ })
210
+
211
+ it('has the expected keys', function () {
212
+ expect(Object.keys(model.fields)).toEqual(['$'])
213
+ })
214
214
  })
215
215
  })
216
216
 
217
- describe('fields', function () {
218
- it('equals expected value', function () {
217
+ describe('required', function () {
218
+ const typeDef = numberType
219
+ const adapters = {
220
+ $: integerToStringAdapter,
221
+ } as const
222
+ let originalValue: ValueOfType<typeof typeDef>
223
+ let model: FormModel<
224
+ typeof typeDef,
225
+ ValueToTypePathsOfType<typeof typeDef>,
226
+ typeof adapters
227
+ >
228
+ beforeEach(function () {
229
+ integerToStringAdapter.convert.mockReturnValue({
230
+ value: 'x',
231
+ required: true,
232
+ readonly: false,
233
+ })
234
+ originalValue = 5
235
+ model = new FormModel<
236
+ typeof typeDef,
237
+ ValueToTypePathsOfType<typeof typeDef>,
238
+ typeof adapters
239
+ >(
240
+ typeDef,
241
+ originalValue,
242
+ adapters,
243
+ )
244
+ })
245
+
246
+ it('reports required status', function () {
219
247
  expect(model.fields).toEqual(
220
248
  expect.objectContaining({
221
249
  $: expect.objectContaining({
222
- value: '5',
250
+ value: 'x',
251
+ required: true,
223
252
  }),
224
253
  }),
225
254
  )
226
255
  })
227
-
228
- it('has the expected keys', function () {
229
- expect(Object.keys(model.fields)).toEqual(['$'])
230
- })
231
256
  })
232
257
  })
233
258
 
@@ -236,10 +261,10 @@ describe('all', function () {
236
261
  const adapters = {
237
262
  '$.*': integerToStringAdapter,
238
263
  } as const
239
- let value: ValueTypeOf<typeof typeDef>
264
+ let value: ValueOfType<typeof typeDef>
240
265
  let model: FormModel<
241
266
  typeof typeDef,
242
- ValueToTypePathsOf<typeof typeDef>,
267
+ ValueToTypePathsOfType<typeof typeDef>,
243
268
  typeof adapters
244
269
  >
245
270
  beforeEach(function () {
@@ -250,7 +275,7 @@ describe('all', function () {
250
275
  ]
251
276
  model = new FormModel<
252
277
  typeof typeDef,
253
- ValueToTypePathsOf<typeof typeDef>,
278
+ ValueToTypePathsOfType<typeof typeDef>,
254
279
  typeof adapters
255
280
  >(
256
281
  typeDef,
@@ -288,6 +313,25 @@ describe('all', function () {
288
313
  ])
289
314
  })
290
315
  })
316
+
317
+ describe('fields', function () {
318
+ it('equals the expected value', function () {
319
+ expect(model.fields).toEqual(expect.objectContaining({
320
+ '$.0': expect.objectContaining({
321
+ value: '1',
322
+ required: false,
323
+ }),
324
+ '$.1': expect.objectContaining({
325
+ value: '4',
326
+ required: false,
327
+ }),
328
+ '$.2': expect.objectContaining({
329
+ value: '17',
330
+ required: false,
331
+ }),
332
+ }))
333
+ })
334
+ })
291
335
  })
292
336
 
293
337
  describe('record', function () {
@@ -296,10 +340,10 @@ describe('all', function () {
296
340
  '$.*': integerToStringAdapter,
297
341
  // '$.*': booleanToBooleanConverter,
298
342
  } as const
299
- let value: ValueTypeOf<typeof typeDef>
343
+ let value: ValueOfType<typeof typeDef>
300
344
  let model: FormModel<
301
345
  typeof typeDef,
302
- ValueToTypePathsOf<typeof typeDef>,
346
+ ValueToTypePathsOfType<typeof typeDef>,
303
347
  typeof converters
304
348
  >
305
349
  beforeEach(function () {
@@ -309,7 +353,7 @@ describe('all', function () {
309
353
  }
310
354
  model = new FormModel<
311
355
  typeof typeDef,
312
- ValueToTypePathsOf<typeof typeDef>,
356
+ ValueToTypePathsOfType<typeof typeDef>,
313
357
  typeof converters
314
358
  >(
315
359
  typeDef,
@@ -360,16 +404,16 @@ describe('all', function () {
360
404
 
361
405
  describe('object', function () {
362
406
  const typeDef = object()
363
- .set('a', numberType)
364
- .set('b', booleanType)
407
+ .field('a', numberType)
408
+ .field('b', booleanType)
365
409
  const converters = {
366
410
  '$.a': integerToStringAdapter,
367
411
  '$.b': booleanToBooleanAdapter,
368
412
  } as const
369
- let value: ValueTypeOf<typeof typeDef>
413
+ let value: ValueOfType<typeof typeDef>
370
414
  let model: FormModel<
371
415
  typeof typeDef,
372
- ValueToTypePathsOf<typeof typeDef>,
416
+ ValueToTypePathsOfType<typeof typeDef>,
373
417
  typeof converters
374
418
  >
375
419
  beforeEach(function () {
@@ -379,7 +423,7 @@ describe('all', function () {
379
423
  }
380
424
  model = new FormModel<
381
425
  typeof typeDef,
382
- ValueToTypePathsOf<typeof typeDef>,
426
+ ValueToTypePathsOfType<typeof typeDef>,
383
427
  typeof converters
384
428
  >(
385
429
  typeDef,
@@ -437,16 +481,16 @@ describe('all', function () {
437
481
  } as const
438
482
  const presenter = new FormPresenter<
439
483
  typeof typeDef,
440
- ValueToTypePathsOf<typeof typeDef>,
484
+ ValueToTypePathsOfType<typeof typeDef>,
441
485
  typeof adapters
442
486
  >(
443
487
  typeDef,
444
488
  adapters,
445
489
  )
446
- const originalValue: ValueTypeOf<typeof typeDef> = 2
490
+ const originalValue: ValueOfType<typeof typeDef> = 2
447
491
  let model: FormModel<
448
492
  typeof typeDef,
449
- ValueToTypePathsOf<typeof typeDef>,
493
+ ValueToTypePathsOfType<typeof typeDef>,
450
494
  typeof adapters
451
495
  >
452
496
  beforeEach(function () {
@@ -467,7 +511,6 @@ describe('all', function () {
467
511
  expect(model.fields).toEqual(expect.objectContaining({
468
512
  $: expect.objectContaining({
469
513
  value: '1',
470
- // eslint-disable-next-line no-undefined
471
514
  error: undefined,
472
515
  }),
473
516
  }))
@@ -499,7 +542,7 @@ describe('all', function () {
499
542
  const errorCode = 65
500
543
  beforeEach(function () {
501
544
  integerToStringAdapter.revert?.mockReturnValueOnce({
502
- type: FieldConversionResult.Failure,
545
+ type: UnreliableFieldConversionType.Failure,
503
546
  error: errorCode,
504
547
  value: [newValue],
505
548
  })
@@ -515,7 +558,7 @@ describe('all', function () {
515
558
  $: expect.objectContaining({
516
559
  value: '-1',
517
560
  error: errorCode,
518
- disabled: false,
561
+ readonly: false,
519
562
  }),
520
563
  })
521
564
  })
@@ -545,7 +588,6 @@ describe('all', function () {
545
588
  expect(model.fields).toEqual(expect.objectContaining({
546
589
  $: expect.objectContaining({
547
590
  value: newValue,
548
- // eslint-disable-next-line no-undefined
549
591
  error: undefined,
550
592
  }),
551
593
  }))
@@ -560,16 +602,16 @@ describe('all', function () {
560
602
  } as const
561
603
  const presenter = new FormPresenter<
562
604
  typeof typeDef,
563
- ValueToTypePathsOf<typeof typeDef>,
605
+ ValueToTypePathsOfType<typeof typeDef>,
564
606
  typeof converters
565
607
  >(
566
608
  typeDef,
567
609
  converters,
568
610
  )
569
- let originalValue: ValueTypeOf<typeof typeDef>
611
+ let originalValue: ValueOfType<typeof typeDef>
570
612
  let model: FormModel<
571
613
  typeof typeDef,
572
- ValueToTypePathsOf<typeof typeDef>,
614
+ ValueToTypePathsOfType<typeof typeDef>,
573
615
  typeof converters
574
616
  >
575
617
  beforeEach(function () {
@@ -599,7 +641,6 @@ describe('all', function () {
599
641
  expect(model.fields).toEqual(expect.objectContaining({
600
642
  '$.0': expect.objectContaining({
601
643
  value: '100',
602
- // eslint-disable-next-line no-undefined
603
644
  error: undefined,
604
645
  }),
605
646
  }))
@@ -642,7 +683,6 @@ describe('all', function () {
642
683
  expect(model.fields).toEqual(expect.objectContaining({
643
684
  '$.0': expect.objectContaining({
644
685
  value: newValue,
645
- // eslint-disable-next-line no-undefined
646
686
  error: undefined,
647
687
  }),
648
688
  }))
@@ -665,7 +705,6 @@ describe('all', function () {
665
705
  }),
666
706
  '$.1': expect.objectContaining({
667
707
  value: '2',
668
- // eslint-disable-next-line no-undefined
669
708
  error: undefined,
670
709
  }),
671
710
  '$.2': expect.objectContaining({
@@ -691,7 +730,7 @@ describe('all', function () {
691
730
  integerToStringAdapter.revert.mockImplementationOnce(function (_value, _path, context) {
692
731
  contextCopy = [...context]
693
732
  return {
694
- type: FieldConversionResult.Success,
733
+ type: UnreliableFieldConversionType.Success,
695
734
  value: 1,
696
735
  }
697
736
  })
@@ -760,7 +799,6 @@ describe('all', function () {
760
799
  it.each([
761
800
  [
762
801
  '$.0',
763
- // eslint-disable-next-line no-undefined
764
802
  undefined,
765
803
  ],
766
804
  [
@@ -881,26 +919,26 @@ describe('all', function () {
881
919
  describe('union', function () {
882
920
  describe('non-discriminated', function () {
883
921
  const listOfNumbersTypeDef = list(numberType)
884
- const typeDef = union()
885
- .add('null', nullType)
886
- .add('0', listOfNumbersTypeDef)
922
+ const type = union()
923
+ .or('null', nullType)
924
+ .or('0', listOfNumbersTypeDef)
887
925
  const adapters = {
888
- $: adapterFromTwoWayConverter(new NullableToBooleanConverter(typeDef, [1])),
926
+ $: adapterFromTwoWayConverter(new NullableToBooleanConverter(type, [1], null)),
889
927
  '$.*': integerToStringAdapter,
890
928
  } as const
891
- type JsonPaths = ValueToTypePathsOf<typeof typeDef>
929
+ type ValueToTypePaths = ValueToTypePathsOfType<typeof type>
892
930
  const presenter = new FormPresenter<
893
- typeof typeDef,
894
- JsonPaths,
931
+ typeof type,
932
+ ValueToTypePaths,
895
933
  typeof adapters
896
934
  >(
897
- typeDef,
935
+ type,
898
936
  adapters,
899
937
  )
900
- let originalValue: ValueTypeOf<typeof typeDef>
938
+ let originalValue: ValueOfType<typeof type>
901
939
  let model: FormModel<
902
- typeof typeDef,
903
- JsonPaths,
940
+ typeof type,
941
+ ValueToTypePaths,
904
942
  typeof adapters
905
943
  >
906
944
  beforeEach(function () {
@@ -911,8 +949,7 @@ describe('all', function () {
911
949
  it('has the expected fields', function () {
912
950
  expect(model.fields).toEqual({
913
951
  $: {
914
- disabled: false,
915
- // eslint-disable-next-line no-undefined
952
+ readonly: false,
916
953
  error: undefined,
917
954
  value: false,
918
955
  required: false,
@@ -932,6 +969,93 @@ describe('all', function () {
932
969
  })
933
970
  })
934
971
  })
972
+
973
+ describe('discriminated', function () {
974
+ const struct1 = object().field('a', numberType)
975
+ const struct2 = object().field('b', booleanType)
976
+ const type = union('d')
977
+ .or('x', struct1)
978
+ .or('y', struct2)
979
+ type ValueToTypePaths = ValueToTypePathsOfType<typeof type>
980
+
981
+ const adapters = {
982
+ $: adapterFromTwoWayConverter(new SelectDiscriminatedUnionConverter(
983
+ type,
984
+ {
985
+ x: {
986
+ d: 'x',
987
+ a: 0,
988
+ },
989
+ y: {
990
+ d: 'y',
991
+ b: false,
992
+ },
993
+ },
994
+ 'x',
995
+ true,
996
+ )).narrow,
997
+ '$.x:a': identityAdapter(0).narrow,
998
+ '$.y:b': identityAdapter(false).narrow,
999
+ } as const
1000
+ const presenter = new FormPresenter<
1001
+ typeof type,
1002
+ ValueToTypePaths,
1003
+ typeof adapters
1004
+ >(
1005
+ type,
1006
+ adapters,
1007
+ )
1008
+
1009
+ describe('isValuePathActive', function () {
1010
+ describe('discriminator x', function () {
1011
+ const model = presenter.createModel({
1012
+ d: 'x',
1013
+ a: 1,
1014
+ })
1015
+ it.each([
1016
+ [
1017
+ '$',
1018
+ true,
1019
+ ],
1020
+ [
1021
+ '$.x:a',
1022
+ true,
1023
+ ],
1024
+ [
1025
+ '$.y:b',
1026
+ false,
1027
+ ],
1028
+ ] as const)('value path %s is active %s', function (path, expected) {
1029
+ const isValid = presenter.isValuePathActive(model, path)
1030
+ expect(isValid).toBe(expected)
1031
+ })
1032
+ })
1033
+
1034
+ describe('discriminator y', function () {
1035
+ const model = presenter.createModel({
1036
+ d: 'y',
1037
+ b: false,
1038
+ })
1039
+ it.each([
1040
+ [
1041
+ '$',
1042
+ true,
1043
+ ],
1044
+ [
1045
+ '$.x:a',
1046
+ false,
1047
+ ],
1048
+ [
1049
+ '$.y:b',
1050
+ true,
1051
+ ],
1052
+ ] as const)('value path %s is active %s', function (path, expected) {
1053
+ const isValid = presenter.isValuePathActive(model, path)
1054
+ expect(isValid).toBe(expected)
1055
+ })
1056
+ })
1057
+ })
1058
+ })
935
1059
  })
936
1060
 
937
1061
  describe('fake', function () {
@@ -952,7 +1076,7 @@ describe('all', function () {
952
1076
  typeDef,
953
1077
  converters,
954
1078
  )
955
- let originalValue: ValueTypeOf<typeof typeDef>
1079
+ let originalValue: ValueOfType<typeof typeDef>
956
1080
  let model: FormModel<
957
1081
  typeof typeDef,
958
1082
  JsonPaths,