@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.
- package/.out/core/mobx/field_adapter.d.ts +7 -6
- package/.out/core/mobx/field_adapter_builder.d.ts +12 -13
- package/.out/core/mobx/field_adapter_builder.js +8 -12
- package/.out/core/mobx/field_adapters_of_values.d.ts +4 -0
- package/.out/core/mobx/flattened_adapters_of_fields.d.ts +2 -2
- package/.out/core/mobx/flattened_list_types_of_type.d.ts +8 -0
- package/.out/core/mobx/form_fields_of_field_adapters.d.ts +8 -0
- package/.out/core/mobx/form_presenter.d.ts +21 -24
- package/.out/core/mobx/form_presenter.js +64 -69
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +13 -0
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.js +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.js +45 -0
- package/.out/core/mobx/specs/fixtures.d.ts +7 -0
- package/.out/core/mobx/specs/fixtures.js +20 -0
- package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +5 -2
- package/.out/core/mobx/specs/{flattened_list_type_defs_of.tests.js → flattened_list_types_of_types.tests.js} +7 -7
- package/.out/core/mobx/specs/form_presenter.tests.js +162 -60
- package/.out/core/mobx/specs/merge_field_adapters_with_two_way_converter.js +89 -0
- package/.out/core/mobx/specs/merge_field_adapters_with_validators.tests.js +172 -0
- package/.out/core/mobx/types.d.ts +2 -2
- package/.out/field_converters/chain_field_converter.d.ts +3 -3
- package/.out/field_converters/chain_field_converter.js +17 -12
- package/.out/field_converters/identity_converter.d.ts +3 -3
- package/.out/field_converters/identity_converter.js +10 -6
- package/.out/field_converters/integer_to_string_converter.d.ts +5 -4
- package/.out/field_converters/integer_to_string_converter.js +13 -6
- package/.out/field_converters/list_converter.d.ts +2 -2
- package/.out/field_converters/list_converter.js +6 -1
- package/.out/field_converters/maybe_identity_converter.d.ts +3 -3
- package/.out/field_converters/maybe_identity_converter.js +3 -1
- package/.out/field_converters/nullable_to_boolean_converter.d.ts +9 -8
- package/.out/field_converters/nullable_to_boolean_converter.js +13 -7
- package/.out/field_converters/select_value_type_converter.d.ts +20 -15
- package/.out/field_converters/select_value_type_converter.js +29 -14
- package/.out/field_converters/specs/chain_field_converter.tests.d.ts +1 -0
- package/.out/field_converters/specs/chain_field_converter.tests.js +251 -0
- package/.out/field_converters/trimming_string_converter.d.ts +3 -3
- package/.out/field_converters/trimming_string_converter.js +7 -3
- package/.out/field_converters/validating_converter.d.ts +3 -3
- package/.out/field_converters/validating_converter.js +7 -5
- package/.out/index.d.ts +9 -2
- package/.out/index.js +9 -2
- package/.out/mantine/create_checkbox.d.ts +2 -3
- package/.out/mantine/create_checkbox.js +6 -5
- package/.out/mantine/create_pill.js +2 -2
- package/.out/mantine/create_radio.js +1 -1
- package/.out/mantine/create_radio_group.d.ts +2 -3
- package/.out/mantine/create_radio_group.js +4 -3
- package/.out/mantine/create_text_input.d.ts +2 -3
- package/.out/mantine/create_text_input.js +6 -5
- package/.out/mantine/create_value_input.d.ts +2 -3
- package/.out/mantine/create_value_input.js +6 -5
- package/.out/mantine/error_renderer.d.ts +6 -0
- package/.out/mantine/error_renderer.js +5 -0
- package/.out/mantine/hooks.d.ts +9 -13
- package/.out/mantine/hooks.js +10 -15
- package/.out/mantine/specs/checkbox_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/checkbox_hooks.stories.js +33 -6
- package/.out/mantine/specs/list_hooks.stories.js +2 -2
- package/.out/mantine/specs/radio_group_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/radio_group_hooks.stories.js +33 -6
- package/.out/mantine/specs/select_hooks.stories.d.ts +8 -2
- package/.out/mantine/specs/select_hooks.stories.js +45 -8
- package/.out/mantine/specs/text_input_hooks.stories.d.ts +5 -1
- package/.out/mantine/specs/text_input_hooks.stories.js +23 -8
- package/.out/mantine/specs/value_input_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/value_input_hooks.stories.js +49 -15
- package/.out/mantine/types.d.ts +4 -1
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/types/error_of_field.d.ts +2 -0
- package/.out/types/error_of_field.js +1 -0
- package/.out/types/field.d.ts +1 -1
- package/.out/types/field_converters.d.ts +17 -10
- package/.out/types/field_converters.js +5 -5
- package/.out/types/flattened_validators_of_fields.d.ts +8 -0
- package/.out/types/flattened_validators_of_fields.js +1 -0
- package/.out/types/merge_validators.d.ts +7 -0
- package/.out/types/merge_validators.js +38 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.d.ts +1 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.js +16 -0
- package/.out/types/specs/merge_validators.tests.d.ts +1 -0
- package/.out/types/specs/merge_validators.tests.js +192 -0
- package/.out/util/partial.d.ts +11 -5
- package/.out/util/partial.js +55 -15
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/README.md +5 -1
- package/core/mobx/field_adapter.ts +15 -7
- package/core/mobx/field_adapter_builder.ts +39 -75
- package/core/mobx/field_adapters_of_values.ts +17 -0
- package/core/mobx/flattened_adapters_of_fields.ts +3 -3
- package/core/mobx/flattened_list_types_of_type.ts +17 -0
- package/core/mobx/form_fields_of_field_adapters.ts +16 -0
- package/core/mobx/form_presenter.ts +117 -104
- package/core/mobx/merge_field_adapters_with_two_way_converter.ts +68 -0
- package/core/mobx/merge_field_adapters_with_validators.ts +99 -0
- package/core/mobx/specs/fixtures.ts +73 -0
- package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +23 -2
- package/core/mobx/specs/flattened_list_types_of_types.tests.ts +35 -0
- package/core/mobx/specs/form_presenter.tests.ts +248 -124
- package/core/mobx/specs/merge_field_adapters_with_two_way_converter.ts +140 -0
- package/core/mobx/specs/merge_field_adapters_with_validators.tests.ts +259 -0
- package/core/mobx/types.ts +3 -3
- package/dist/index.cjs +459 -211
- package/dist/index.d.cts +153 -111
- package/dist/index.d.ts +153 -111
- package/dist/index.js +453 -200
- package/field_converters/chain_field_converter.ts +37 -23
- package/field_converters/identity_converter.ts +14 -10
- package/field_converters/integer_to_string_converter.ts +15 -9
- package/field_converters/list_converter.ts +8 -3
- package/field_converters/maybe_identity_converter.ts +7 -4
- package/field_converters/nullable_to_boolean_converter.ts +23 -16
- package/field_converters/select_value_type_converter.ts +86 -26
- package/field_converters/specs/chain_field_converter.tests.ts +302 -0
- package/field_converters/trimming_string_converter.ts +11 -6
- package/field_converters/validating_converter.ts +21 -11
- package/index.ts +9 -2
- package/mantine/create_checkbox.tsx +15 -8
- package/mantine/create_list.tsx +1 -4
- package/mantine/create_pill.tsx +2 -2
- package/mantine/create_radio.tsx +1 -1
- package/mantine/create_radio_group.tsx +8 -6
- package/mantine/create_text_input.tsx +20 -8
- package/mantine/create_value_input.tsx +17 -8
- package/mantine/error_renderer.ts +15 -0
- package/mantine/hooks.tsx +25 -51
- package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +126 -0
- package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +356 -0
- package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +208 -12
- package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +45 -0
- package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +194 -8
- package/mantine/specs/checkbox_hooks.stories.tsx +47 -7
- package/mantine/specs/list_hooks.stories.tsx +2 -2
- package/mantine/specs/radio_group_hooks.stories.tsx +47 -7
- package/mantine/specs/select_hooks.stories.tsx +55 -8
- package/mantine/specs/text_input_hooks.stories.tsx +32 -7
- package/mantine/specs/value_input_hooks.stories.tsx +57 -16
- package/mantine/types.ts +5 -1
- package/package.json +16 -4
- package/tsconfig.json +1 -0
- package/types/error_of_field.ts +3 -0
- package/types/field.ts +1 -1
- package/types/field_converters.ts +21 -10
- package/types/flattened_validators_of_fields.ts +34 -0
- package/types/merge_validators.ts +80 -0
- package/types/specs/error_type_of_field.tests.ts +2 -2
- package/types/specs/flattened_validators_of_fields.tests.ts +93 -0
- package/types/specs/merge_validators.tests.ts +267 -0
- package/util/partial.tsx +200 -16
- package/.out/core/mobx/flattened_list_type_defs_of.d.ts +0 -8
- package/.out/field_validators/minimum_string_length_field_validator.d.ts +0 -2
- package/.out/field_validators/minimum_string_length_field_validator.js +0 -8
- package/.out/types/error_type_of_field.d.ts +0 -2
- package/.out/types/field_validator.d.ts +0 -3
- package/.out/types/flattened_form_fields_of.d.ts +0 -9
- package/.out/types/specs/flattened_form_fields_of.tests.js +0 -13
- package/core/mobx/flattened_list_type_defs_of.ts +0 -17
- package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +0 -35
- package/field_validators/minimum_string_length_field_validator.ts +0 -13
- package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +0 -227
- package/types/error_type_of_field.ts +0 -3
- package/types/field_validator.ts +0 -7
- package/types/flattened_form_fields_of.ts +0 -16
- package/types/specs/flattened_form_fields_of.tests.ts +0 -43
- /package/.out/core/mobx/{flattened_list_type_defs_of.js → field_adapters_of_values.js} +0 -0
- /package/.out/core/mobx/{specs/flattened_list_type_defs_of.tests.d.ts → flattened_list_types_of_type.js} +0 -0
- /package/.out/{types/error_type_of_field.js → core/mobx/form_fields_of_field_adapters.js} +0 -0
- /package/.out/{types/field_validator.js → core/mobx/specs/flattened_list_types_of_types.tests.d.ts} +0 -0
- /package/.out/{types/flattened_form_fields_of.js → core/mobx/specs/merge_field_adapters_with_two_way_converter.d.ts} +0 -0
- /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 @@
|
|
|
1
|
+
export {};
|
package/.out/types/field.d.ts
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
import { type Maybe } from '@strictly/base';
|
|
2
|
-
export declare enum
|
|
2
|
+
export declare enum UnreliableFieldConversionType {
|
|
3
3
|
Success = 0,
|
|
4
4
|
Failure = 1
|
|
5
5
|
}
|
|
6
|
-
export type
|
|
7
|
-
type:
|
|
6
|
+
export type UnreliableFieldConversion<V = any, E = any> = {
|
|
7
|
+
type: UnreliableFieldConversionType.Success;
|
|
8
8
|
value: V;
|
|
9
9
|
} | {
|
|
10
|
-
type:
|
|
10
|
+
type: UnreliableFieldConversionType.Failure;
|
|
11
11
|
error: E;
|
|
12
12
|
value: Maybe<V>;
|
|
13
13
|
};
|
|
14
|
-
export type
|
|
15
|
-
(from: From, valuePath: ValuePath, context: Context):
|
|
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
|
|
18
|
-
|
|
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:
|
|
22
|
-
revert:
|
|
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
|
|
2
|
-
(function (
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
})(
|
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
});
|
package/.out/util/partial.d.ts
CHANGED
|
@@ -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
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function
|
|
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 {};
|
package/.out/util/partial.js
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
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
|
-
|
|
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
|
}
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -3,16 +3,16 @@ $ tsup
|
|
|
3
3
|
[34mCLI[39m Building entry: index.ts
|
|
4
4
|
[34mCLI[39m Using tsconfig: tsconfig.build.json
|
|
5
5
|
[34mCLI[39m tsup v8.3.5
|
|
6
|
-
[34mCLI[39m Using tsup config: /home/runner/work/
|
|
6
|
+
[34mCLI[39m Using tsup config: /home/runner/work/strictly/strictly/packages/react-form/tsup.config.ts
|
|
7
7
|
[34mCLI[39m Target: esnext
|
|
8
8
|
[34mCJS[39m Build start
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[34mDTS[39m Build start
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
18
|
-
Done in
|
|
11
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m373.16 KB[39m
|
|
12
|
+
[32mCJS[39m ⚡️ Build success in 1665ms
|
|
13
|
+
[32mESM[39m [1mdist/index.js [22m[32m363.03 KB[39m
|
|
14
|
+
[32mESM[39m ⚡️ Build success in 1708ms
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 10720ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m34.05 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m34.05 KB[39m
|
|
18
|
+
Done in 12.03s.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
2
|
+
type AnnotatedFieldConverter,
|
|
3
3
|
type FieldValueFactory,
|
|
4
|
-
type
|
|
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:
|
|
19
|
+
readonly convert: AnnotatedFieldConverter<From, To, ValuePath, Context>,
|
|
20
20
|
readonly create: FieldValueFactory<From, ValuePath, Context>,
|
|
21
|
-
readonly revert?:
|
|
21
|
+
readonly revert?: UnreliableFieldConverter<To, From, E, ValuePath, Context>,
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export type
|
|
24
|
+
export type FromOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer From> ? From
|
|
25
25
|
: never
|
|
26
26
|
|
|
27
|
-
export type
|
|
27
|
+
export type ToOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _F, infer To> ? To
|
|
28
28
|
: never
|
|
29
29
|
|
|
30
|
-
export type
|
|
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
|