@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
@@ -0,0 +1,2 @@
1
+ import { type Field } from './field';
2
+ export type ErrorOfField<F extends Field> = F extends Field<infer _V, infer E> ? E : never;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,7 @@
1
1
  export type Field<V = any, E = any> = {
2
2
  readonly value: V;
3
3
  readonly error?: E | undefined;
4
- readonly disabled: boolean;
4
+ readonly readonly: boolean;
5
5
  readonly required: boolean;
6
6
  };
7
7
  export type Fields = Readonly<Record<string, Field>>;
@@ -1,25 +1,32 @@
1
1
  import { type Maybe } from '@strictly/base';
2
- export declare enum FieldConversionResult {
2
+ export declare enum UnreliableFieldConversionType {
3
3
  Success = 0,
4
4
  Failure = 1
5
5
  }
6
- export type FieldConversion<V, E> = {
7
- type: FieldConversionResult.Success;
6
+ export type UnreliableFieldConversion<V = any, E = any> = {
7
+ type: UnreliableFieldConversionType.Success;
8
8
  value: V;
9
9
  } | {
10
- type: FieldConversionResult.Failure;
10
+ type: UnreliableFieldConversionType.Failure;
11
11
  error: E;
12
12
  value: Maybe<V>;
13
13
  };
14
- export type FieldConverter<From, To, E, ValuePath extends string, Context> = {
15
- (from: From, valuePath: ValuePath, context: Context): FieldConversion<To, E>;
14
+ export type UnreliableFieldConverter<From, To, E, ValuePath extends string, Context> = {
15
+ (from: From, valuePath: ValuePath, context: Context): UnreliableFieldConversion<To, E>;
16
16
  };
17
- export type SafeFieldConverter<From, To, ValuePath extends string, Context> = {
18
- (from: From, valuePath: ValuePath, context: Context): To;
17
+ export type Annotation = {
18
+ readonly required: boolean;
19
+ readonly readonly: boolean;
20
+ };
21
+ export type AnnotatedFieldConversion<V = any> = {
22
+ value: V;
23
+ } & Annotation;
24
+ export type AnnotatedFieldConverter<From, To, ValuePath extends string, Context> = {
25
+ (from: From, valuePath: ValuePath, context: Context): AnnotatedFieldConversion<To>;
19
26
  };
20
27
  export type TwoWayFieldConverter<From, To, E, ValuePath extends string, Context> = {
21
- convert: SafeFieldConverter<From, To, ValuePath, Context>;
22
- revert: FieldConverter<To, From, E, ValuePath, Context>;
28
+ convert: AnnotatedFieldConverter<From, To, ValuePath, Context>;
29
+ revert: UnreliableFieldConverter<To, From, E, ValuePath, Context>;
23
30
  };
24
31
  export type FieldValueFactory<V, ValuePath extends string, Context> = {
25
32
  (valuePath: ValuePath, context: Context): V;
@@ -1,5 +1,5 @@
1
- export var FieldConversionResult;
2
- (function (FieldConversionResult) {
3
- FieldConversionResult[FieldConversionResult["Success"] = 0] = "Success";
4
- FieldConversionResult[FieldConversionResult["Failure"] = 1] = "Failure";
5
- })(FieldConversionResult || (FieldConversionResult = {}));
1
+ export var UnreliableFieldConversionType;
2
+ (function (UnreliableFieldConversionType) {
3
+ UnreliableFieldConversionType[UnreliableFieldConversionType["Success"] = 0] = "Success";
4
+ UnreliableFieldConversionType[UnreliableFieldConversionType["Failure"] = 1] = "Failure";
5
+ })(UnreliableFieldConversionType || (UnreliableFieldConversionType = {}));
@@ -0,0 +1,8 @@
1
+ import { type ReadonlyTypeOfType, type Type, type Validator, type ValueOfType } from '@strictly/define';
2
+ import { type SimplifyDeep, type ValueOf } from 'type-fest';
3
+ import { type Field } from 'types/field';
4
+ export type FlattenedValidatorsOfFields<ValuePathsToTypePaths extends Readonly<Record<string, string>>, FlattenedTypeDefs extends Partial<Readonly<Record<ValueOf<ValuePathsToTypePaths>, Type>>>, FormFields extends Partial<Readonly<Record<keyof ValuePathsToTypePaths, Field>>>> = SimplifyDeep<{
5
+ readonly [K in keyof ValuePathsToTypePaths as FormFields[K] extends Field ? ValuePathsToTypePaths[K] : never]: ValidatorOfField<NonNullable<FormFields[K]>, FlattenedTypeDefs[ValuePathsToTypePaths[K]], K>;
6
+ }>;
7
+ type ValidatorOfField<F extends Field, T extends Type | undefined, ValuePath extends string | number | symbol> = ValuePath extends string ? F extends Field<infer V, infer E> ? undefined extends T ? Validator<V, E, ValuePath> : Validator<ValueOfType<ReadonlyTypeOfType<NonNullable<T>>>, E, ValuePath> : never : never;
8
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { type Validator } from '@strictly/define';
2
+ import { type Simplify } from 'type-fest';
3
+ export type MergedOfValidators<Validators1 extends Partial<Readonly<Record<Keys, Validator>>>, Validators2 extends Partial<Readonly<Record<Keys, Validator>>>, Keys extends string = Extract<keyof Validators1 | keyof Validators2, string>> = Simplify<{
4
+ readonly [K in Keys]: undefined extends Validators1[K] ? undefined extends Validators2[K] ? never : Validators2[K] : undefined extends Validators2[K] ? Validators1[K] : MergedOfValidator<NonNullable<Validators1[K]>, NonNullable<Validators2[K]>>;
5
+ }>;
6
+ export type MergedOfValidator<Validator1 extends Validator, Validator2 extends Validator> = Validator1 extends Validator<infer V, infer E1, infer P, infer C> ? Validator2 extends Validator<V, infer E2, P, C> ? Validator<V, E1 | E2, P, C> : never : never;
7
+ export declare function mergeValidators<Validators1 extends Partial<Readonly<Record<Keys, Validator>>>, Validators2 extends Partial<Readonly<Record<Keys, Validator>>>, Keys extends string = Extract<keyof Validators1 | keyof Validators2, string>>(validators1: Validators1, validators2: Validators2): MergedOfValidators<Validators1, Validators2, Keys>;
@@ -0,0 +1,38 @@
1
+ import { annotations, validate, } from '@strictly/define';
2
+ export function mergeValidators(validators1, validators2) {
3
+ const validators = {
4
+ ...validators1,
5
+ ...validators2,
6
+ };
7
+ const keys1 = new Set(Object.keys(validators1));
8
+ const keys2 = new Set(Object.keys(validators2));
9
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
+ return Array.from(keys1.intersection(keys2)).reduce(function (validators, key) {
11
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12
+ const validator1 = validators1[key];
13
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
14
+ const validator2 = validators2[key];
15
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
16
+ validators[key] = {
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ validate: function (value, valuePath, context) {
19
+ const error = validate(validator1, value, valuePath, context);
20
+ if (error != null) {
21
+ return error;
22
+ }
23
+ return validate(validator2, value, valuePath, context);
24
+ },
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ annotations: function (valuePath, context) {
27
+ const annotations1 = annotations(validator1, valuePath, context);
28
+ const annotations2 = annotations(validator2, valuePath, context);
29
+ return {
30
+ readonly: annotations1.readonly || annotations2.readonly,
31
+ required: annotations1.required || annotations2.required,
32
+ };
33
+ },
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ };
36
+ return validators;
37
+ }, validators);
38
+ }
@@ -0,0 +1,16 @@
1
+ const error = Symbol();
2
+ describe('FlattenedValidatorsOfFields', function () {
3
+ it('maps the converter types', function () {
4
+ expectTypeOf().toEqualTypeOf();
5
+ });
6
+ it('ignores extraneous types not listed in the fields', function () {
7
+ expectTypeOf().toEqualTypeOf();
8
+ });
9
+ it('handles multiple fields', function () {
10
+ expectTypeOf().toEqualTypeOf();
11
+ });
12
+ it('allows synthesized fields', function () {
13
+ expectTypeOf().toEqualTypeOf();
14
+ });
15
+ });
16
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,192 @@
1
+ import { annotations, validate, } from '@strictly/define';
2
+ import { mergeValidators, } from 'types/merge_validators';
3
+ import { mock, mockReset, } from 'vitest-mock-extended';
4
+ describe('MergedOfValidators', function () {
5
+ describe('empty validators 1', function () {
6
+ it('equals the expected type', function () {
7
+ expectTypeOf().toEqualTypeOf();
8
+ });
9
+ });
10
+ describe('empty validators 2', function () {
11
+ it('equals the expected type', function () {
12
+ expectTypeOf().toEqualTypeOf();
13
+ });
14
+ });
15
+ describe('merged validators with different keys', function () {
16
+ it('equals the expected type', function () {
17
+ expectTypeOf().toEqualTypeOf();
18
+ });
19
+ });
20
+ describe('merged validators with same key', function () {
21
+ it('equals the expected type', function () {
22
+ expectTypeOf().toEqualTypeOf();
23
+ });
24
+ });
25
+ });
26
+ describe('mergeValidators', function () {
27
+ describe('functional validators', function () {
28
+ const validatorA1 = vi.fn();
29
+ const validatorA2 = vi.fn();
30
+ const validatorB = vi.fn();
31
+ describe('produces expected type', function () {
32
+ describe('empty validators 1', function () {
33
+ const validators1 = {
34
+ a: validatorA1,
35
+ b: validatorB,
36
+ };
37
+ const validators2 = {};
38
+ const validators = mergeValidators(validators1, validators2);
39
+ it('equals expected value', function () {
40
+ expect(validators).toEqual(validators1);
41
+ });
42
+ });
43
+ describe('empty validators 2', function () {
44
+ const validators1 = {};
45
+ const validators2 = {
46
+ a: validatorA1,
47
+ b: validatorB,
48
+ };
49
+ const validators = mergeValidators(validators1, validators2);
50
+ it('equals expected value', function () {
51
+ expect(validators).toEqual(validators2);
52
+ });
53
+ });
54
+ describe('merged validators with different keys', function () {
55
+ const validators1 = {
56
+ a: validatorA1,
57
+ };
58
+ const validators2 = {
59
+ b: validatorB,
60
+ };
61
+ const validators = mergeValidators(validators1, validators2);
62
+ it('equals expected value', function () {
63
+ expect(validators).toEqual({
64
+ a: validatorA1,
65
+ b: validatorB,
66
+ });
67
+ });
68
+ });
69
+ describe('merged validators with same key', function () {
70
+ const validators1 = {
71
+ a: validatorA1,
72
+ };
73
+ const validators2 = {
74
+ a: validatorA2,
75
+ };
76
+ const validators = mergeValidators(validators1, validators2);
77
+ it('has the expected keys', function () {
78
+ expect(Array.from(Object.keys(validators))).toEqual(['a']);
79
+ });
80
+ it('reports no error when validators report no error', function () {
81
+ const result = validate(validators.a, 'x', 'a', null);
82
+ expect(result).toBeUndefined();
83
+ });
84
+ it('reports an error from first validator', function () {
85
+ validatorA1.mockReturnValueOnce('error a1');
86
+ const result = validate(validators.a, 'x', 'a', null);
87
+ expect(result).toEqual('error a1');
88
+ });
89
+ it('reports an error from second validator', function () {
90
+ validatorA2.mockReturnValueOnce('error a2');
91
+ const result = validate(validators.a, 'x', 'a', null);
92
+ expect(result).toEqual('error a2');
93
+ });
94
+ });
95
+ });
96
+ });
97
+ describe('annotated validators', function () {
98
+ const validatorA1 = mock();
99
+ const validatorA2 = mock();
100
+ beforeEach(function () {
101
+ mockReset(validatorA1);
102
+ mockReset(validatorA2);
103
+ });
104
+ const validators1 = {
105
+ a: validatorA1,
106
+ };
107
+ const validators2 = {
108
+ a: validatorA2,
109
+ };
110
+ const validators = mergeValidators(validators1, validators2);
111
+ describe.each([
112
+ [
113
+ false,
114
+ false,
115
+ false,
116
+ ],
117
+ [
118
+ false,
119
+ true,
120
+ true,
121
+ ],
122
+ [
123
+ true,
124
+ false,
125
+ true,
126
+ ],
127
+ [
128
+ true,
129
+ true,
130
+ true,
131
+ ],
132
+ ])('required', function (required1, required2, required) {
133
+ beforeEach(function () {
134
+ validatorA1.validate.mockReturnValue('error a1');
135
+ validatorA1.annotations.mockReturnValue({
136
+ required: required1,
137
+ readonly: false,
138
+ });
139
+ validatorA2.annotations.mockReturnValue({
140
+ required: required2,
141
+ readonly: false,
142
+ });
143
+ });
144
+ it('has the expected required value', function () {
145
+ expect(annotations(validators.a, 'a', null)).toEqual({
146
+ required,
147
+ readonly: false,
148
+ });
149
+ });
150
+ });
151
+ describe.each([
152
+ [
153
+ false,
154
+ false,
155
+ false,
156
+ ],
157
+ [
158
+ false,
159
+ true,
160
+ true,
161
+ ],
162
+ [
163
+ true,
164
+ false,
165
+ true,
166
+ ],
167
+ [
168
+ true,
169
+ true,
170
+ true,
171
+ ],
172
+ ])('required', function (readonly1, readonly2, readonly) {
173
+ beforeEach(function () {
174
+ validatorA1.validate.mockReturnValue('error a1');
175
+ validatorA1.annotations.mockReturnValue({
176
+ required: false,
177
+ readonly: readonly1,
178
+ });
179
+ validatorA2.annotations.mockReturnValue({
180
+ required: false,
181
+ readonly: readonly2,
182
+ });
183
+ });
184
+ it('has the expected required value', function () {
185
+ expect(annotations(validators.a, 'a', null)).toEqual({
186
+ required: false,
187
+ readonly,
188
+ });
189
+ });
190
+ });
191
+ });
192
+ });
@@ -1,11 +1,17 @@
1
+ import { type FriendlyExhaustiveArrayOfUnion } from '@strictly/base';
1
2
  import { type ComponentProps, type ComponentType, type DependencyList, type ForwardRefExoticComponent, type PropsWithoutRef } from 'react';
2
3
  export type PartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}> = Exclude<keyof CurriedProps, keyof ComponentProps<Component>> extends never ? UnsafePartialComponent<Component, CurriedProps, AdditionalProps> : keyof CurriedProps extends (string | number) ? `unmatched prop: ${Exclude<keyof CurriedProps, keyof ComponentProps<Component>>}` : Exclude<keyof CurriedProps, keyof ComponentProps<Component>>;
3
4
  export type UnsafePartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}> = ForwardRefExoticComponent<PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>>;
4
5
  export declare function createSimplePartialComponent<Component extends ComponentType<any>, CurriedProps extends Partial<ComponentProps<Component>>>(Component: Component, curriedProps: CurriedProps): PartialComponent<Component, CurriedProps>;
5
- export declare function createPartialComponent<Component extends ComponentType<any>, CurriedProps extends Partial<ComponentProps<Component>>, AdditionalProps = {}>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps): PartialComponent<Component, CurriedProps, AdditionalProps>;
6
- export declare function usePartialComponent<Component extends ComponentType<any>, CurriedProps extends Partial<ComponentProps<Component>>, AdditionalProps = {}>(createCurriedProps: (additionalProps: AdditionalProps) => CurriedProps, deps: DependencyList, Component: Component): PartialComponent<Component, CurriedProps, AdditionalProps>;
7
- export declare function createPartialObserverComponent<Component extends ComponentType<any>, CurriedProps extends Partial<ComponentProps<Component>>, AdditionalProps = {}>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps): PartialComponent<Component, CurriedProps, AdditionalProps>;
8
- export declare function createUnsafePartialObserverComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps): UnsafePartialComponent<Component, CurriedProps, AdditionalProps>;
9
- export declare function usePartialObserverComponent<Component extends ComponentType<any>, CurriedProps extends Partial<ComponentProps<Component>>, AdditionalProps = {}>(curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, deps: DependencyList, Component: Component): PartialComponent<Component, CurriedProps, AdditionalProps>;
6
+ export declare function createPartialComponent<Component extends ComponentType<any>, CurriedProps>(Component: Component, curriedPropsSource: () => CurriedProps): PartialComponent<Component, CurriedProps, {}>;
7
+ export declare function createPartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps, AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[]>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>): PartialComponent<Component, CurriedProps, AdditionalProps>;
8
+ export declare function usePartialComponent<Component extends ComponentType<any>, CurriedProps>(curriedPropsSource: () => CurriedProps, deps: DependencyList, Component: Component): PartialComponent<Component, CurriedProps, {}>;
9
+ export declare function usePartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps, AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[]>(curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, deps: DependencyList, Component: Component, additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>): PartialComponent<Component, CurriedProps, AdditionalProps>;
10
+ export declare function createPartialObserverComponent<Component extends ComponentType<any>, CurriedProps>(Component: Component, curriedPropsSource: () => CurriedProps): PartialComponent<Component, CurriedProps, {}>;
11
+ export declare function createPartialObserverComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps, AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[]>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>): PartialComponent<Component, CurriedProps, AdditionalProps>;
12
+ export declare function createUnsafePartialObserverComponent<Component extends ComponentType<any>, CurriedProps>(Component: Component, curriedPropsSource: () => CurriedProps): UnsafePartialComponent<Component, CurriedProps, {}>;
13
+ export declare function createUnsafePartialObserverComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps, AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[]>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>): UnsafePartialComponent<Component, CurriedProps, AdditionalProps>;
14
+ export declare function usePartialObserverComponent<Component extends ComponentType<any>, CurriedProps>(curriedPropsSource: () => CurriedProps, deps: DependencyList, Component: Component): PartialComponent<Component, CurriedProps, {}>;
15
+ export declare function usePartialObserverComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps, AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[]>(curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, deps: DependencyList, Component: Component, additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>): PartialComponent<Component, CurriedProps, AdditionalProps>;
10
16
  type RemainingComponentProps<Component extends ComponentType, CurriedProps> = Omit<ComponentProps<Component>, keyof CurriedProps> & JSX.IntrinsicAttributes;
11
17
  export {};
@@ -11,49 +11,87 @@ export function createSimplePartialComponent(Component, curriedProps) {
11
11
  return (_jsx(C, { ref: ref, ...curriedProps, ...exposedProps }));
12
12
  });
13
13
  }
14
- export function createPartialComponent(Component, curriedPropsSource) {
14
+ export function createPartialComponent(Component, curriedPropsSource, additionalPropKeys = []) {
15
15
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
16
- return forwardRef(function (exposedProps, ref) {
16
+ return forwardRef(function (props, ref) {
17
17
  // forward ref types are really difficult to work with
18
18
  // still needs a cast as `extends ComponentType<any>` != `ComponentType<any>`
19
19
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-assertion
20
20
  const C = Component;
21
+ const [additionalProps, exposedProps,] = additionalPropKeys.reduce(function ([additionalProps, exposedProps,], key) {
22
+ const value = props[
23
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
24
+ key];
25
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
26
+ delete exposedProps[key];
27
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
28
+ additionalProps[key] = value;
29
+ return [
30
+ additionalProps,
31
+ exposedProps,
32
+ ];
33
+ }, [
34
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
35
+ {},
36
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37
+ { ...props },
38
+ ]);
21
39
  // TODO is there any way we can memoize this transformation?
22
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
23
- const curriedProps = curriedPropsSource(exposedProps);
40
+ const curriedProps = curriedPropsSource(additionalProps);
24
41
  return (_jsx(C, { ref: ref, ...curriedProps, ...exposedProps }));
25
42
  });
26
43
  }
27
44
  export function usePartialComponent(
28
45
  // has to be first so eslint react-hooks/exhaustive-deps can find the callback
29
46
  // has to be a function so eslint react-hooks/exhaustive-deps can reason about it :(
30
- createCurriedProps,
47
+ curriedPropsSource,
31
48
  // has to be next so eslint react-hooks/exhaustive-deps can find the deps
32
- deps, Component) {
49
+ deps, Component, additionalPropKeys = []) {
33
50
  return useMemo(function () {
34
- return createPartialComponent(Component, createCurriedProps);
51
+ return createPartialComponent(Component, curriedPropsSource, additionalPropKeys);
35
52
  },
36
53
  // eslint-disable-next-line react-hooks/exhaustive-deps
37
54
  [
38
55
  // eslint-disable-next-line react-hooks/exhaustive-deps
39
56
  ...deps,
40
57
  Component,
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ ...additionalPropKeys,
41
60
  ]);
42
61
  }
43
- export function createPartialObserverComponent(Component, curriedPropsSource) {
62
+ export function createPartialObserverComponent(Component, curriedPropsSource, additionalPropKeys = []) {
44
63
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
45
- return createUnsafePartialObserverComponent(Component, curriedPropsSource);
64
+ return createUnsafePartialObserverComponent(Component, curriedPropsSource, additionalPropKeys);
46
65
  }
47
- export function createUnsafePartialObserverComponent(Component, curriedPropsSource) {
66
+ export function createUnsafePartialObserverComponent(Component, curriedPropsSource, additionalPropKeys = []) {
48
67
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
49
- return observer(forwardRef(function (exposedProps, ref) {
68
+ return observer(forwardRef(function (props, ref) {
50
69
  // forward ref types are really difficult to work with
51
70
  // still needs a cast as `extends ComponentType<any>` != `ComponentType<any>`
52
71
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-assertion
53
72
  const C = Component;
73
+ // remove the additional props from the exposed props that get passed in to the component
74
+ // as this generates react warnings
75
+ const [additionalProps, exposedProps,] = additionalPropKeys.reduce(function ([additionalProps, exposedProps,], key) {
76
+ const value = props[
77
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
78
+ key];
79
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
80
+ delete exposedProps[key];
81
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
82
+ additionalProps[key] = value;
83
+ return [
84
+ additionalProps,
85
+ exposedProps,
86
+ ];
87
+ }, [
88
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
89
+ {},
90
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
91
+ { ...props },
92
+ ]);
54
93
  // TODO is there any way we can memoize this transformation?
55
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
56
- const curriedProps = curriedPropsSource(exposedProps);
94
+ const curriedProps = curriedPropsSource(additionalProps);
57
95
  return (_jsx(C, { ref: ref, ...curriedProps, ...exposedProps }));
58
96
  }));
59
97
  }
@@ -61,14 +99,16 @@ export function usePartialObserverComponent(
61
99
  // has to be first so eslint react-hooks/exhaustive-deps can find the callback
62
100
  curriedPropsSource,
63
101
  // has to be next so eslint react-hooks/exhaustive-deps can find the deps
64
- deps, Component) {
102
+ deps, Component, additionalPropKeys = []) {
65
103
  return useMemo(function () {
66
- return createPartialObserverComponent(Component, curriedPropsSource);
104
+ return createPartialObserverComponent(Component, curriedPropsSource, additionalPropKeys);
67
105
  },
68
106
  // eslint-disable-next-line react-hooks/exhaustive-deps
69
107
  [
70
108
  // eslint-disable-next-line react-hooks/exhaustive-deps
71
109
  ...deps,
72
110
  Component,
111
+ // eslint-disable-next-line react-hooks/exhaustive-deps
112
+ ...additionalPropKeys,
73
113
  ]);
74
114
  }
@@ -3,16 +3,16 @@ $ tsup
3
3
  CLI Building entry: index.ts
4
4
  CLI Using tsconfig: tsconfig.build.json
5
5
  CLI tsup v8.3.5
6
- CLI Using tsup config: /home/runner/work/de/de/packages/react-form/tsup.config.ts
6
+ CLI Using tsup config: /home/runner/work/strictly/strictly/packages/react-form/tsup.config.ts
7
7
  CLI Target: esnext
8
8
  CJS Build start
9
9
  ESM Build start
10
10
  DTS Build start
11
- ESM dist/index.js 356.07 KB
12
- ESM ⚡️ Build success in 1306ms
13
- CJS dist/index.cjs 365.80 KB
14
- CJS ⚡️ Build success in 1307ms
15
- DTS ⚡️ Build success in 10156ms
16
- DTS dist/index.d.cts 26.90 KB
17
- DTS dist/index.d.ts 26.90 KB
18
- Done in 11.09s.
11
+ CJS dist/index.cjs 373.16 KB
12
+ CJS ⚡️ Build success in 1665ms
13
+ ESM dist/index.js 363.03 KB
14
+ ESM ⚡️ Build success in 1708ms
15
+ DTS ⚡️ Build success in 10720ms
16
+ DTS dist/index.d.cts 34.05 KB
17
+ DTS dist/index.d.ts 34.05 KB
18
+ Done in 12.03s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc
3
- Done in 6.90s.
3
+ Done in 7.83s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ json -f package.json -f package.exports.json --merge > package.release.json
3
- Done in 0.09s.
3
+ Done in 0.10s.
package/README.md CHANGED
@@ -1,2 +1,6 @@
1
- # Form (React and Mobx)
1
+ React form types and tools
2
2
 
3
+ # TODO
4
+
5
+ * Move Mobx handling to separate package
6
+ * Move Mantine hooks to separate package
@@ -1,7 +1,7 @@
1
1
  import {
2
- type FieldConverter,
2
+ type AnnotatedFieldConverter,
3
3
  type FieldValueFactory,
4
- type SafeFieldConverter,
4
+ type UnreliableFieldConverter,
5
5
  } from 'types/field_converters'
6
6
 
7
7
  export type FieldAdapter<
@@ -16,17 +16,25 @@ export type FieldAdapter<
16
16
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
17
  Context = any,
18
18
  > = {
19
- readonly convert: SafeFieldConverter<From, To, ValuePath, Context>,
19
+ readonly convert: AnnotatedFieldConverter<From, To, ValuePath, Context>,
20
20
  readonly create: FieldValueFactory<From, ValuePath, Context>,
21
- readonly revert?: FieldConverter<To, From, E, ValuePath, Context>,
21
+ readonly revert?: UnreliableFieldConverter<To, From, E, ValuePath, Context>,
22
22
  }
23
23
 
24
- export type FromTypeOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer From> ? From
24
+ export type FromOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer From> ? From
25
25
  : never
26
26
 
27
- export type ToTypeOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _F, infer To> ? To
27
+ export type ToOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _F, infer To> ? To
28
28
  : never
29
29
 
30
- export type ErrorTypeOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _From, infer _To, infer E>
30
+ export type ErrorOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _From, infer _To, infer E>
31
31
  ? NonNullable<E>
32
32
  : never
33
+
34
+ export type ValuePathOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<
35
+ infer _From,
36
+ infer _To,
37
+ infer _E,
38
+ infer ValuePath
39
+ > ? ValuePath
40
+ : never