@strictly/react-form 0.0.1 → 0.0.3

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 +527 -10412
  107. package/dist/index.d.cts +153 -111
  108. package/dist/index.d.ts +153 -111
  109. package/dist/index.js +527 -10420
  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 +20 -7
  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
@@ -0,0 +1,20 @@
1
+ import { mock, mockReset, } from 'vitest-mock-extended';
2
+ export function createMockedAdapter(_original) {
3
+ return mock();
4
+ }
5
+ export function resetMockAdapter({ convert, revert, create, }, mockedAdapter) {
6
+ mockReset(mockedAdapter);
7
+ if (revert) {
8
+ mockedAdapter.revert?.mockImplementation(revert);
9
+ }
10
+ mockedAdapter.convert.mockImplementation(convert);
11
+ mockedAdapter.create.mockImplementation(create);
12
+ }
13
+ export function createMockTwoWayFieldConverter(_original) {
14
+ return mock();
15
+ }
16
+ export function resetMockTwoWayFieldConverter({ convert, revert, }, mockedConverter) {
17
+ mockReset(mockedConverter);
18
+ mockedConverter.convert.mockImplementation(convert);
19
+ mockedConverter.revert.mockImplementation(revert);
20
+ }
@@ -1,12 +1,15 @@
1
1
  const error = Symbol();
2
- describe('FlattenedAdaptersOfFIelds', function () {
2
+ describe('FlattenedAdaptersOfFields', function () {
3
3
  it('maps the converter types', function () {
4
4
  expectTypeOf().toEqualTypeOf();
5
5
  });
6
6
  it('ignores extraneous types not listed in the fields', function () {
7
7
  expectTypeOf().toEqualTypeOf();
8
8
  });
9
- it('handles multiple fields fields', function () {
9
+ it('handles multiple fields', function () {
10
+ expectTypeOf().toEqualTypeOf();
11
+ });
12
+ it('allows synthesized fields', function () {
10
13
  expectTypeOf().toEqualTypeOf();
11
14
  });
12
15
  });
@@ -1,16 +1,16 @@
1
1
  import { list, nullType, numberType, object, record, stringType, union, } from '@strictly/define';
2
- describe('FlattenedListTypeDefsOf', function () {
2
+ describe('FlattenedListTypesOfType', function () {
3
3
  it('filters lists types', function () {
4
4
  const listTypeDef = list(numberType);
5
5
  const recordTypeDef = record(stringType);
6
6
  const objectTypeDef = object();
7
- const unionTypeDef = union().add('0', numberType).add('1', nullType);
7
+ const unionTypeDef = union().or('0', numberType).or('1', nullType);
8
8
  const typeDef = object()
9
- .set('literal', numberType)
10
- .set('list', listTypeDef)
11
- .set('record', recordTypeDef)
12
- .set('object', objectTypeDef)
13
- .set('union', unionTypeDef);
9
+ .field('literal', numberType)
10
+ .field('list', listTypeDef)
11
+ .field('record', recordTypeDef)
12
+ .field('object', objectTypeDef)
13
+ .field('union', unionTypeDef);
14
14
  expectTypeOf().toEqualTypeOf();
15
15
  });
16
16
  });
@@ -4,25 +4,19 @@ import { adapterFromTwoWayConverter, identityAdapter, } from 'core/mobx/field_ad
4
4
  import { FormModel, FormPresenter, } from 'core/mobx/form_presenter';
5
5
  import { IntegerToStringConverter } from 'field_converters/integer_to_string_converter';
6
6
  import { NullableToBooleanConverter } from 'field_converters/nullable_to_boolean_converter';
7
+ import { SelectDiscriminatedUnionConverter } from 'field_converters/select_value_type_converter';
7
8
  import { prototypingFieldValueFactory } from 'field_value_factories/prototyping_field_value_factory';
8
- import { FieldConversionResult, } from 'types/field_converters';
9
- import { mock, mockClear, } from 'vitest-mock-extended';
9
+ import { UnreliableFieldConversionType, } from 'types/field_converters';
10
+ import { createMockedAdapter, resetMockAdapter, } from './fixtures';
10
11
  const IS_NAN_ERROR = 1;
11
- function createMockedAdapter({ convert, revert, create, }) {
12
- const mockedAdapter = mock();
13
- if (revert) {
14
- mockedAdapter.revert?.mockImplementation(revert);
15
- }
16
- mockedAdapter.convert.mockImplementation(convert);
17
- mockedAdapter.create.mockImplementation(create);
18
- return mockedAdapter;
19
- }
12
+ const originalIntegerToStringAdapter = adapterFromTwoWayConverter(new IntegerToStringConverter(IS_NAN_ERROR), prototypingFieldValueFactory(0));
13
+ const originalBooleanToBooleanAdapter = identityAdapter(false);
20
14
  describe('all', function () {
21
- const integerToStringAdapter = createMockedAdapter(adapterFromTwoWayConverter(new IntegerToStringConverter(IS_NAN_ERROR), prototypingFieldValueFactory(0)));
22
- const booleanToBooleanAdapter = createMockedAdapter(identityAdapter(false));
15
+ const integerToStringAdapter = createMockedAdapter(originalIntegerToStringAdapter);
16
+ const booleanToBooleanAdapter = createMockedAdapter(originalBooleanToBooleanAdapter);
23
17
  beforeEach(function () {
24
- mockClear(integerToStringAdapter);
25
- mockClear(booleanToBooleanAdapter);
18
+ resetMockAdapter(originalIntegerToStringAdapter, integerToStringAdapter);
19
+ resetMockAdapter(originalBooleanToBooleanAdapter, booleanToBooleanAdapter);
26
20
  });
27
21
  describe('FlattenedTypePathsToConvertersOf', function () {
28
22
  describe('record', function () {
@@ -34,8 +28,8 @@ describe('all', function () {
34
28
  });
35
29
  describe('object', function () {
36
30
  const typeDef = object()
37
- .set('x', stringType)
38
- .set('y', booleanType);
31
+ .field('x', stringType)
32
+ .field('y', booleanType);
39
33
  let t;
40
34
  it('equals expected type', function () {
41
35
  expectTypeOf(t).toEqualTypeOf();
@@ -64,39 +58,66 @@ describe('all', function () {
64
58
  });
65
59
  describe('FormModel', function () {
66
60
  describe('literal', function () {
67
- const typeDef = numberType;
68
- const adapters = {
69
- $: integerToStringAdapter,
70
- };
71
- let originalValue;
72
- let model;
73
- beforeEach(function () {
74
- originalValue = 5;
75
- model = new FormModel(typeDef, originalValue, adapters);
76
- });
77
- describe('accessors', function () {
78
- it('gets the expected value', function () {
79
- const accessor = expectDefinedAndReturn(model.accessors.$);
80
- expect(accessor.value).toEqual(originalValue);
61
+ describe('optional', function () {
62
+ const typeDef = numberType;
63
+ const adapters = {
64
+ $: integerToStringAdapter,
65
+ };
66
+ let originalValue;
67
+ let model;
68
+ beforeEach(function () {
69
+ originalValue = 5;
70
+ model = new FormModel(typeDef, originalValue, adapters);
81
71
  });
82
- it('sets the underlying value', function () {
83
- const newValue = 1;
84
- const accessor = expectDefinedAndReturn(model.accessors.$);
85
- accessor.set(newValue);
86
- expect(model.value).toEqual(newValue);
72
+ describe('accessors', function () {
73
+ it('gets the expected value', function () {
74
+ const accessor = expectDefinedAndReturn(model.accessors.$);
75
+ expect(accessor.value).toEqual(originalValue);
76
+ });
77
+ it('sets the underlying value', function () {
78
+ const newValue = 1;
79
+ const accessor = expectDefinedAndReturn(model.accessors.$);
80
+ accessor.set(newValue);
81
+ expect(model.value).toEqual(newValue);
82
+ });
83
+ });
84
+ describe('fields', function () {
85
+ it('equals expected value', function () {
86
+ expect(model.fields).toEqual(expect.objectContaining({
87
+ $: expect.objectContaining({
88
+ value: '5',
89
+ }),
90
+ }));
91
+ });
92
+ it('has the expected keys', function () {
93
+ expect(Object.keys(model.fields)).toEqual(['$']);
94
+ });
87
95
  });
88
96
  });
89
- describe('fields', function () {
90
- it('equals expected value', function () {
97
+ describe('required', function () {
98
+ const typeDef = numberType;
99
+ const adapters = {
100
+ $: integerToStringAdapter,
101
+ };
102
+ let originalValue;
103
+ let model;
104
+ beforeEach(function () {
105
+ integerToStringAdapter.convert.mockReturnValue({
106
+ value: 'x',
107
+ required: true,
108
+ readonly: false,
109
+ });
110
+ originalValue = 5;
111
+ model = new FormModel(typeDef, originalValue, adapters);
112
+ });
113
+ it('reports required status', function () {
91
114
  expect(model.fields).toEqual(expect.objectContaining({
92
115
  $: expect.objectContaining({
93
- value: '5',
116
+ value: 'x',
117
+ required: true,
94
118
  }),
95
119
  }));
96
120
  });
97
- it('has the expected keys', function () {
98
- expect(Object.keys(model.fields)).toEqual(['$']);
99
- });
100
121
  });
101
122
  });
102
123
  describe('list', function () {
@@ -142,6 +163,24 @@ describe('all', function () {
142
163
  ]);
143
164
  });
144
165
  });
166
+ describe('fields', function () {
167
+ it('equals the expected value', function () {
168
+ expect(model.fields).toEqual(expect.objectContaining({
169
+ '$.0': expect.objectContaining({
170
+ value: '1',
171
+ required: false,
172
+ }),
173
+ '$.1': expect.objectContaining({
174
+ value: '4',
175
+ required: false,
176
+ }),
177
+ '$.2': expect.objectContaining({
178
+ value: '17',
179
+ required: false,
180
+ }),
181
+ }));
182
+ });
183
+ });
145
184
  });
146
185
  describe('record', function () {
147
186
  const typeDef = record(numberType);
@@ -194,8 +233,8 @@ describe('all', function () {
194
233
  });
195
234
  describe('object', function () {
196
235
  const typeDef = object()
197
- .set('a', numberType)
198
- .set('b', booleanType);
236
+ .field('a', numberType)
237
+ .field('b', booleanType);
199
238
  const converters = {
200
239
  '$.a': integerToStringAdapter,
201
240
  '$.b': booleanToBooleanAdapter,
@@ -268,7 +307,6 @@ describe('all', function () {
268
307
  expect(model.fields).toEqual(expect.objectContaining({
269
308
  $: expect.objectContaining({
270
309
  value: '1',
271
- // eslint-disable-next-line no-undefined
272
310
  error: undefined,
273
311
  }),
274
312
  }));
@@ -296,7 +334,7 @@ describe('all', function () {
296
334
  const errorCode = 65;
297
335
  beforeEach(function () {
298
336
  integerToStringAdapter.revert?.mockReturnValueOnce({
299
- type: FieldConversionResult.Failure,
337
+ type: UnreliableFieldConversionType.Failure,
300
338
  error: errorCode,
301
339
  value: [newValue],
302
340
  });
@@ -310,7 +348,7 @@ describe('all', function () {
310
348
  $: expect.objectContaining({
311
349
  value: '-1',
312
350
  error: errorCode,
313
- disabled: false,
351
+ readonly: false,
314
352
  }),
315
353
  });
316
354
  });
@@ -337,7 +375,6 @@ describe('all', function () {
337
375
  expect(model.fields).toEqual(expect.objectContaining({
338
376
  $: expect.objectContaining({
339
377
  value: newValue,
340
- // eslint-disable-next-line no-undefined
341
378
  error: undefined,
342
379
  }),
343
380
  }));
@@ -376,7 +413,6 @@ describe('all', function () {
376
413
  expect(model.fields).toEqual(expect.objectContaining({
377
414
  '$.0': expect.objectContaining({
378
415
  value: '100',
379
- // eslint-disable-next-line no-undefined
380
416
  error: undefined,
381
417
  }),
382
418
  }));
@@ -413,7 +449,6 @@ describe('all', function () {
413
449
  expect(model.fields).toEqual(expect.objectContaining({
414
450
  '$.0': expect.objectContaining({
415
451
  value: newValue,
416
- // eslint-disable-next-line no-undefined
417
452
  error: undefined,
418
453
  }),
419
454
  }));
@@ -434,7 +469,6 @@ describe('all', function () {
434
469
  }),
435
470
  '$.1': expect.objectContaining({
436
471
  value: '2',
437
- // eslint-disable-next-line no-undefined
438
472
  error: undefined,
439
473
  }),
440
474
  '$.2': expect.objectContaining({
@@ -458,7 +492,7 @@ describe('all', function () {
458
492
  integerToStringAdapter.revert.mockImplementationOnce(function (_value, _path, context) {
459
493
  contextCopy = [...context];
460
494
  return {
461
- type: FieldConversionResult.Success,
495
+ type: UnreliableFieldConversionType.Success,
462
496
  value: 1,
463
497
  };
464
498
  });
@@ -517,7 +551,6 @@ describe('all', function () {
517
551
  it.each([
518
552
  [
519
553
  '$.0',
520
- // eslint-disable-next-line no-undefined
521
554
  undefined,
522
555
  ],
523
556
  [
@@ -624,14 +657,14 @@ describe('all', function () {
624
657
  describe('union', function () {
625
658
  describe('non-discriminated', function () {
626
659
  const listOfNumbersTypeDef = list(numberType);
627
- const typeDef = union()
628
- .add('null', nullType)
629
- .add('0', listOfNumbersTypeDef);
660
+ const type = union()
661
+ .or('null', nullType)
662
+ .or('0', listOfNumbersTypeDef);
630
663
  const adapters = {
631
- $: adapterFromTwoWayConverter(new NullableToBooleanConverter(typeDef, [1])),
664
+ $: adapterFromTwoWayConverter(new NullableToBooleanConverter(type, [1], null)),
632
665
  '$.*': integerToStringAdapter,
633
666
  };
634
- const presenter = new FormPresenter(typeDef, adapters);
667
+ const presenter = new FormPresenter(type, adapters);
635
668
  let originalValue;
636
669
  let model;
637
670
  beforeEach(function () {
@@ -641,8 +674,7 @@ describe('all', function () {
641
674
  it('has the expected fields', function () {
642
675
  expect(model.fields).toEqual({
643
676
  $: {
644
- disabled: false,
645
- // eslint-disable-next-line no-undefined
677
+ readonly: false,
646
678
  error: undefined,
647
679
  value: false,
648
680
  required: false,
@@ -660,6 +692,76 @@ describe('all', function () {
660
692
  });
661
693
  });
662
694
  });
695
+ describe('discriminated', function () {
696
+ const struct1 = object().field('a', numberType);
697
+ const struct2 = object().field('b', booleanType);
698
+ const type = union('d')
699
+ .or('x', struct1)
700
+ .or('y', struct2);
701
+ const adapters = {
702
+ $: adapterFromTwoWayConverter(new SelectDiscriminatedUnionConverter(type, {
703
+ x: {
704
+ d: 'x',
705
+ a: 0,
706
+ },
707
+ y: {
708
+ d: 'y',
709
+ b: false,
710
+ },
711
+ }, 'x', true)).narrow,
712
+ '$.x:a': identityAdapter(0).narrow,
713
+ '$.y:b': identityAdapter(false).narrow,
714
+ };
715
+ const presenter = new FormPresenter(type, adapters);
716
+ describe('isValuePathActive', function () {
717
+ describe('discriminator x', function () {
718
+ const model = presenter.createModel({
719
+ d: 'x',
720
+ a: 1,
721
+ });
722
+ it.each([
723
+ [
724
+ '$',
725
+ true,
726
+ ],
727
+ [
728
+ '$.x:a',
729
+ true,
730
+ ],
731
+ [
732
+ '$.y:b',
733
+ false,
734
+ ],
735
+ ])('value path %s is active %s', function (path, expected) {
736
+ const isValid = presenter.isValuePathActive(model, path);
737
+ expect(isValid).toBe(expected);
738
+ });
739
+ });
740
+ describe('discriminator y', function () {
741
+ const model = presenter.createModel({
742
+ d: 'y',
743
+ b: false,
744
+ });
745
+ it.each([
746
+ [
747
+ '$',
748
+ true,
749
+ ],
750
+ [
751
+ '$.x:a',
752
+ false,
753
+ ],
754
+ [
755
+ '$.y:b',
756
+ true,
757
+ ],
758
+ ])('value path %s is active %s', function (path, expected) {
759
+ const isValid = presenter.isValuePathActive(model, path);
760
+ expect(isValid).toBe(expected);
761
+ });
762
+ });
763
+ });
764
+ });
663
765
  });
664
766
  describe('fake', function () {
665
767
  const typeDef = numberType;
@@ -0,0 +1,89 @@
1
+ import { identityAdapter } from 'core/mobx/field_adapter_builder';
2
+ import { mergeFieldAdaptersWithTwoWayConverter, } from 'core/mobx/merge_field_adapters_with_two_way_converter';
3
+ import { annotatedIdentityConverter, unreliableIdentityConverter, } from 'field_converters/identity_converter';
4
+ import { UnreliableFieldConversionType, } from 'types/field_converters';
5
+ import { createMockedAdapter, createMockTwoWayFieldConverter, resetMockAdapter, resetMockTwoWayFieldConverter, } from './fixtures';
6
+ const error1 = Symbol();
7
+ const error2 = Symbol();
8
+ const error3 = Symbol();
9
+ const error4 = Symbol();
10
+ const context = Symbol();
11
+ describe('MergedOfFieldAdapterWithTwoWayConverter', function () {
12
+ let m;
13
+ it('merges the errors', function () {
14
+ expectTypeOf().toEqualTypeOf(m);
15
+ });
16
+ });
17
+ const originalIntegerAdapter = identityAdapter(0);
18
+ const originalBooleanAdapter = identityAdapter(false, true);
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ const originalConverter = {
21
+ convert: annotatedIdentityConverter(),
22
+ revert: unreliableIdentityConverter(),
23
+ };
24
+ describe('mergeFieldAdaptersWithTwoWayConverter', function () {
25
+ const integerAdapter = createMockedAdapter(originalIntegerAdapter);
26
+ const booleanAdapter = createMockedAdapter(originalBooleanAdapter);
27
+ beforeEach(function () {
28
+ resetMockAdapter(originalIntegerAdapter, integerAdapter);
29
+ resetMockAdapter(originalBooleanAdapter, booleanAdapter);
30
+ });
31
+ describe('two entries', function () {
32
+ const fieldAdapters = {
33
+ integerAdapter,
34
+ booleanAdapter,
35
+ };
36
+ const converter = createMockTwoWayFieldConverter(originalConverter);
37
+ beforeEach(function () {
38
+ resetMockTwoWayFieldConverter(originalConverter, converter);
39
+ });
40
+ const merged = mergeFieldAdaptersWithTwoWayConverter(fieldAdapters, converter);
41
+ describe('convert', function () {
42
+ let result;
43
+ describe('success', function () {
44
+ // note don't really need to exercise this too extensively since most of
45
+ // the work is done in chainXFieldAdapter
46
+ beforeEach(function () {
47
+ result = merged.booleanAdapter.convert(true, 'booleanAdapter', context);
48
+ });
49
+ it('returns the same value on convert', function () {
50
+ expect(result).toEqual(expect.objectContaining({
51
+ value: true,
52
+ }));
53
+ });
54
+ it('calls the mocked converter', function () {
55
+ expect(converter.convert).toHaveBeenCalledOnce();
56
+ expect(converter.convert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
57
+ });
58
+ it('calls the mocked adapter', function () {
59
+ expect(booleanAdapter.convert).toHaveBeenCalledOnce();
60
+ expect(booleanAdapter.convert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
61
+ });
62
+ });
63
+ });
64
+ describe('revert', function () {
65
+ let result;
66
+ describe('success', function () {
67
+ // note don't really need to exercise this too extensively since most of
68
+ // the work is done in chainXFieldAdapter
69
+ beforeEach(function () {
70
+ result = merged.booleanAdapter.revert(true, 'booleanAdapter', context);
71
+ });
72
+ });
73
+ it('returns the same value on revert', function () {
74
+ expect(result).toEqual(expect.objectContaining({
75
+ value: true,
76
+ type: UnreliableFieldConversionType.Success,
77
+ }));
78
+ });
79
+ it('calls the mocked converter', function () {
80
+ expect(converter.revert).toHaveBeenCalledOnce();
81
+ expect(converter.revert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
82
+ });
83
+ it('calls the mocked adapter', function () {
84
+ expect(booleanAdapter.revert).toHaveBeenCalledOnce();
85
+ expect(booleanAdapter.revert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
86
+ });
87
+ });
88
+ });
89
+ });
@@ -0,0 +1,172 @@
1
+ import { expectDefined, expectEquals, } from '@strictly/base';
2
+ import { identityAdapter, } from 'core/mobx/field_adapter_builder';
3
+ import { mergeAdaptersWithValidators, } from 'core/mobx/merge_field_adapters_with_validators';
4
+ import { UnreliableFieldConversionType } from 'types/field_converters';
5
+ import { createMockedAdapter, resetMockAdapter, } from './fixtures';
6
+ const error1 = 'error 1';
7
+ const error2 = 'error 2';
8
+ const context = 'context 1';
9
+ describe('MergedOfFieldAdaptersWithValidators', function () {
10
+ describe('empty validators', function () {
11
+ it('does not change the adapters', function () {
12
+ expectTypeOf().toEqualTypeOf();
13
+ });
14
+ });
15
+ describe('different errors', function () {
16
+ it('merges the error types', function () {
17
+ expectTypeOf().toEqualTypeOf();
18
+ });
19
+ });
20
+ describe('different paths', function () {
21
+ it('merges the error types', function () {
22
+ expectTypeOf().toEqualTypeOf();
23
+ });
24
+ });
25
+ describe('different values', function () {
26
+ it('removes mismatched values', function () {
27
+ expectTypeOf().toEqualTypeOf();
28
+ });
29
+ });
30
+ });
31
+ const originalIntegerToIntegerAdapter = identityAdapter(0);
32
+ const originalBooleanToBooleanAdapter = identityAdapter(false, true);
33
+ describe('mergeFieldAdaptersWithValidators', function () {
34
+ const integerToIntegerAdapter = createMockedAdapter(originalIntegerToIntegerAdapter);
35
+ const booleanToBooleanAdapter = createMockedAdapter(originalBooleanToBooleanAdapter);
36
+ const failingValidator1 = vi.fn(function () {
37
+ return 'fail 1';
38
+ });
39
+ const failingValidator2 = vi.fn(function () {
40
+ return 'fail 2';
41
+ });
42
+ const requiredValidator = {
43
+ validate: () => null,
44
+ annotations: () => ({
45
+ required: true,
46
+ readonly: false,
47
+ }),
48
+ };
49
+ const readonlyValidator = {
50
+ validate: () => null,
51
+ annotations: () => ({
52
+ required: false,
53
+ readonly: true,
54
+ }),
55
+ };
56
+ beforeEach(function () {
57
+ resetMockAdapter(originalIntegerToIntegerAdapter, integerToIntegerAdapter);
58
+ resetMockAdapter(originalBooleanToBooleanAdapter, booleanToBooleanAdapter);
59
+ failingValidator1.mockClear();
60
+ failingValidator2.mockClear();
61
+ });
62
+ describe('record contents', function () {
63
+ describe('empty validators', function () {
64
+ const adapters = {
65
+ a: integerToIntegerAdapter,
66
+ b: booleanToBooleanAdapter,
67
+ };
68
+ const validators = {};
69
+ const merged = mergeAdaptersWithValidators(adapters, validators);
70
+ it('does not change the adapters', function () {
71
+ expect(merged).toEqual(adapters);
72
+ });
73
+ });
74
+ describe('populated validators', function () {
75
+ const adapters = {
76
+ a: integerToIntegerAdapter,
77
+ b: booleanToBooleanAdapter,
78
+ c: integerToIntegerAdapter,
79
+ d: integerToIntegerAdapter,
80
+ };
81
+ const validators = {
82
+ a: failingValidator1,
83
+ b: failingValidator2,
84
+ c: requiredValidator,
85
+ d: readonlyValidator,
86
+ };
87
+ const merged = mergeAdaptersWithValidators(adapters, validators);
88
+ describe('matching validators', function () {
89
+ it('has the same keys', function () {
90
+ expect([...Object.keys(adapters)]).toEqual([
91
+ 'a',
92
+ 'b',
93
+ 'c',
94
+ 'd',
95
+ ]);
96
+ });
97
+ });
98
+ describe('revert', function () {
99
+ it.each([
100
+ [
101
+ 'a',
102
+ 'fail 1',
103
+ 1,
104
+ ],
105
+ [
106
+ 'b',
107
+ 'fail 2',
108
+ true,
109
+ ],
110
+ ])('field %s fails with validation %s', function (key, error, value) {
111
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
+ const mergedAdapter = merged[key];
113
+ expectDefined(mergedAdapter.revert);
114
+ const result = mergedAdapter.revert(value, key, null);
115
+ expectEquals(result.type, UnreliableFieldConversionType.Failure);
116
+ expect(result.error).toEqual(error);
117
+ });
118
+ it.each([
119
+ [
120
+ 'c',
121
+ 1,
122
+ ],
123
+ [
124
+ 'd',
125
+ true,
126
+ ],
127
+ ])('field %s succeeds with value %s', function (key, value) {
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ const mergedAdapter = merged[key];
130
+ expectDefined(mergedAdapter.revert);
131
+ const result = mergedAdapter.revert(value, key, null);
132
+ expectEquals(result.type, UnreliableFieldConversionType.Success);
133
+ expect(result.value).toEqual(value);
134
+ });
135
+ });
136
+ describe('convert', function () {
137
+ it.each([
138
+ [
139
+ 'a',
140
+ false,
141
+ false,
142
+ 1,
143
+ ],
144
+ [
145
+ 'b',
146
+ true,
147
+ false,
148
+ true,
149
+ ],
150
+ [
151
+ 'c',
152
+ true,
153
+ false,
154
+ 2,
155
+ ],
156
+ [
157
+ 'd',
158
+ false,
159
+ true,
160
+ 3,
161
+ ],
162
+ ])('field %s is required %s and readonly %s', function (key, expectedRequired, expectedReadonly, value) {
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
164
+ const adapter = merged[key];
165
+ const { required, readonly, } = adapter.convert(value, key, null);
166
+ expect(required).toEqual(expectedRequired);
167
+ expect(readonly).toEqual(expectedReadonly);
168
+ });
169
+ });
170
+ });
171
+ });
172
+ });