@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.
- 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 +527 -10412
- package/dist/index.d.cts +153 -111
- package/dist/index.d.ts +153 -111
- package/dist/index.js +527 -10420
- 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 +20 -7
- 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
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '@storybook/react'
|
|
16
16
|
import { type FormProps } from 'core/props'
|
|
17
17
|
import { type SuppliedValueInputProps } from 'mantine/create_value_input'
|
|
18
|
+
import { type ErrorRenderer } from 'mantine/error_renderer'
|
|
18
19
|
import { useMantineForm } from 'mantine/hooks'
|
|
19
20
|
import {
|
|
20
21
|
type ComponentType,
|
|
@@ -33,14 +34,17 @@ function Component<
|
|
|
33
34
|
P extends StoryValueInputProps<V>,
|
|
34
35
|
>({
|
|
35
36
|
ValueInput,
|
|
36
|
-
|
|
37
|
+
ErrorRenderer,
|
|
38
|
+
inputProps,
|
|
37
39
|
...props
|
|
38
40
|
}: FormProps<{
|
|
39
41
|
$: Field<V, string>,
|
|
40
42
|
}> & {
|
|
41
43
|
ValueInput: ComponentType<P>,
|
|
42
44
|
} & {
|
|
43
|
-
|
|
45
|
+
inputProps: P,
|
|
46
|
+
} & {
|
|
47
|
+
ErrorRenderer?: ErrorRenderer,
|
|
44
48
|
}) {
|
|
45
49
|
const form = useMantineForm(props)
|
|
46
50
|
const ValueInputComponent = form.valueInput<'$', P>('$', ValueInput)
|
|
@@ -48,8 +52,9 @@ function Component<
|
|
|
48
52
|
<ValueInputComponent
|
|
49
53
|
{
|
|
50
54
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions
|
|
51
|
-
...
|
|
55
|
+
...inputProps as any
|
|
52
56
|
}
|
|
57
|
+
ErrorRenderer={ErrorRenderer}
|
|
53
58
|
/>
|
|
54
59
|
)
|
|
55
60
|
}
|
|
@@ -75,13 +80,13 @@ export const EmptyNumberInput: Story<number | string, NumberInputProps> = {
|
|
|
75
80
|
args: {
|
|
76
81
|
fields: {
|
|
77
82
|
$: {
|
|
78
|
-
|
|
79
|
-
required:
|
|
83
|
+
readonly: false,
|
|
84
|
+
required: false,
|
|
80
85
|
value: '',
|
|
81
86
|
},
|
|
82
87
|
},
|
|
83
88
|
ValueInput: NumberInput,
|
|
84
|
-
|
|
89
|
+
inputProps: {
|
|
85
90
|
label: NUMBER_INPUT_LABEL,
|
|
86
91
|
},
|
|
87
92
|
},
|
|
@@ -91,13 +96,49 @@ export const PopulatedNumberInput: Story<number | string, NumberInputProps> = {
|
|
|
91
96
|
args: {
|
|
92
97
|
fields: {
|
|
93
98
|
$: {
|
|
94
|
-
|
|
99
|
+
readonly: false,
|
|
100
|
+
required: false,
|
|
101
|
+
value: 3,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
ValueInput: NumberInput,
|
|
105
|
+
inputProps: {
|
|
106
|
+
label: NUMBER_INPUT_LABEL,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const RequiredNumberInput: Story<number | string, NumberInputProps> = {
|
|
112
|
+
args: {
|
|
113
|
+
fields: {
|
|
114
|
+
$: {
|
|
115
|
+
readonly: false,
|
|
116
|
+
required: true,
|
|
117
|
+
value: 3,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
ValueInput: NumberInput,
|
|
121
|
+
inputProps: {
|
|
122
|
+
label: NUMBER_INPUT_LABEL,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const CustomErrorNumberInput: Story<number | string, NumberInputProps> = {
|
|
128
|
+
args: {
|
|
129
|
+
fields: {
|
|
130
|
+
$: {
|
|
131
|
+
readonly: false,
|
|
95
132
|
required: false,
|
|
96
133
|
value: 3,
|
|
134
|
+
error: 'an error',
|
|
97
135
|
},
|
|
98
136
|
},
|
|
99
137
|
ValueInput: NumberInput,
|
|
100
|
-
|
|
138
|
+
ErrorRenderer: function () {
|
|
139
|
+
return 'a custom error'
|
|
140
|
+
},
|
|
141
|
+
inputProps: {
|
|
101
142
|
label: NUMBER_INPUT_LABEL,
|
|
102
143
|
},
|
|
103
144
|
},
|
|
@@ -107,13 +148,13 @@ export const DisabledNumberInput: Story<number | string, NumberInputProps> = {
|
|
|
107
148
|
args: {
|
|
108
149
|
fields: {
|
|
109
150
|
$: {
|
|
110
|
-
|
|
151
|
+
readonly: true,
|
|
111
152
|
required: false,
|
|
112
153
|
value: 3,
|
|
113
154
|
},
|
|
114
155
|
},
|
|
115
156
|
ValueInput: NumberInput,
|
|
116
|
-
|
|
157
|
+
inputProps: {
|
|
117
158
|
label: NUMBER_INPUT_LABEL,
|
|
118
159
|
},
|
|
119
160
|
},
|
|
@@ -123,13 +164,13 @@ export const AnSlider: Story<number, SliderProps> = {
|
|
|
123
164
|
args: {
|
|
124
165
|
fields: {
|
|
125
166
|
$: {
|
|
126
|
-
|
|
167
|
+
readonly: false,
|
|
127
168
|
required: false,
|
|
128
169
|
value: 3,
|
|
129
170
|
},
|
|
130
171
|
},
|
|
131
172
|
ValueInput: Slider,
|
|
132
|
-
|
|
173
|
+
inputProps: {
|
|
133
174
|
label: SLIDER_LABEL,
|
|
134
175
|
min: 1,
|
|
135
176
|
max: 10,
|
|
@@ -155,13 +196,13 @@ export const AnRating: Story<number, RatingProps> = {
|
|
|
155
196
|
args: {
|
|
156
197
|
fields: {
|
|
157
198
|
$: {
|
|
158
|
-
|
|
199
|
+
readonly: false,
|
|
159
200
|
required: false,
|
|
160
201
|
value: 2,
|
|
161
202
|
},
|
|
162
203
|
},
|
|
163
204
|
ValueInput: Rating,
|
|
164
|
-
|
|
205
|
+
inputProps: {},
|
|
165
206
|
},
|
|
166
207
|
}
|
|
167
208
|
|
|
@@ -169,13 +210,13 @@ export const AnJsonInput: Story<string, JsonInputProps> = {
|
|
|
169
210
|
args: {
|
|
170
211
|
fields: {
|
|
171
212
|
$: {
|
|
172
|
-
|
|
213
|
+
readonly: false,
|
|
173
214
|
required: false,
|
|
174
215
|
value: '{}',
|
|
175
216
|
},
|
|
176
217
|
},
|
|
177
218
|
ValueInput: JsonInput,
|
|
178
|
-
|
|
219
|
+
inputProps: {
|
|
179
220
|
rows: 8,
|
|
180
221
|
},
|
|
181
222
|
},
|
package/mantine/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ComponentType } from 'react'
|
|
2
2
|
import { type Fields } from 'types/field'
|
|
3
3
|
import { type UnsafePartialComponent } from 'util/partial'
|
|
4
|
+
import { type ErrorRenderer } from './error_renderer'
|
|
4
5
|
|
|
5
6
|
export type MantineForm<F extends Fields> = {
|
|
6
7
|
fields: F,
|
|
@@ -10,4 +11,7 @@ export type MantineForm<F extends Fields> = {
|
|
|
10
11
|
onFieldSubmit: ((this: void, key: keyof F) => boolean | void) | undefined,
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
export type MantineFieldComponent<T, P = T, E = any> = UnsafePartialComponent<ComponentType<P>, T, {
|
|
16
|
+
ErrorRenderer?: ErrorRenderer<E>,
|
|
17
|
+
}>
|
package/package.json
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
+
"author": "Chris <chris.glover@gmail.com> (@madmaw)",
|
|
2
3
|
"dependencies": {
|
|
4
|
+
"@mantine/core": "^7.13.4",
|
|
5
|
+
"@mantine/hooks": "^7.13.4",
|
|
3
6
|
"@strictly/base": "*",
|
|
4
7
|
"@strictly/define": "*",
|
|
5
8
|
"mobx": "^6.13.5",
|
|
6
9
|
"mobx-react": "^9.1.1",
|
|
7
|
-
"react": "^19.0.0"
|
|
10
|
+
"react": "^19.0.0 || ^18.3.1",
|
|
11
|
+
"react-dom": "^19.0.0 || ^18.3.1"
|
|
8
12
|
},
|
|
13
|
+
"description": "Types and utilities for creating React forms",
|
|
9
14
|
"devDependencies": {
|
|
10
15
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
11
16
|
"@babel/plugin-transform-class-static-block": "^7.26.0",
|
|
12
|
-
"@mantine/core": "^7.13.4",
|
|
13
17
|
"@storybook/addon-actions": "^8.4.5",
|
|
14
18
|
"@storybook/addon-essentials": "^8.4.5",
|
|
15
19
|
"@storybook/addon-interactions": "^8.4.5",
|
|
@@ -18,21 +22,27 @@
|
|
|
18
22
|
"@storybook/builder-vite": "^8.4.5",
|
|
19
23
|
"@storybook/react": "^8.4.5",
|
|
20
24
|
"@storybook/react-vite": "^8.4.5",
|
|
21
|
-
"@storybook/test-runner": "^0.
|
|
25
|
+
"@storybook/test-runner": "^0.21.0",
|
|
22
26
|
"@strictly/support-vite": "*",
|
|
23
27
|
"@testing-library/dom": "^10.4.0",
|
|
24
28
|
"@testing-library/react": "^16.0.1",
|
|
25
29
|
"@types/react": "^18.3.12",
|
|
26
30
|
"@types/react-dom": "^18.3.1",
|
|
27
31
|
"@vitejs/plugin-react": "^4.3.3",
|
|
32
|
+
"concurrently": "^9.1.2",
|
|
28
33
|
"jsdom": "^25.0.1",
|
|
29
|
-
"react-dom": "^19.0.0",
|
|
30
34
|
"resize-observer-polyfill": "^1.5.1",
|
|
31
35
|
"storybook": "^8.4.5",
|
|
32
36
|
"type-fest": "^4.26.1",
|
|
33
37
|
"vite": "^6.0.5",
|
|
34
38
|
"vitest-matchmedia-mock": "^1.0.6"
|
|
35
39
|
},
|
|
40
|
+
"homepage": "https://madmaw.github.io/strictly/form",
|
|
41
|
+
"keywords": [
|
|
42
|
+
"react",
|
|
43
|
+
"state management",
|
|
44
|
+
"types"
|
|
45
|
+
],
|
|
36
46
|
"license": "MIT",
|
|
37
47
|
"name": "@strictly/react-form",
|
|
38
48
|
"publishConfig": {
|
|
@@ -41,22 +51,25 @@
|
|
|
41
51
|
"repository": {
|
|
42
52
|
"directory": "packages/react-form",
|
|
43
53
|
"type": "git",
|
|
44
|
-
"url": "git+https://github.com/madmaw/
|
|
54
|
+
"url": "git+https://github.com/madmaw/strictly.git"
|
|
45
55
|
},
|
|
46
56
|
"scripts": {
|
|
47
57
|
"build": "tsup",
|
|
48
58
|
"check-types": "tsc",
|
|
49
|
-
"clean": "del-cli dist",
|
|
59
|
+
"clean": "del-cli dist .out .turbo",
|
|
50
60
|
"lint": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint . --max-warnings=0",
|
|
51
61
|
"lint:fix": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint . --fix",
|
|
52
62
|
"release:exports": "json -f package.json -f package.exports.json --merge > package.release.json",
|
|
53
63
|
"storybook": "storybook dev",
|
|
64
|
+
"storybook:build": "storybook build",
|
|
65
|
+
"storybook:test": "yarn playwright install && yarn storybook:build && concurrently -k -s first -n \"SB,TEST\" \"npx http-server storybook-static --port 6006 --silent\" \"npx wait-on tcp:127.0.0.1:6006 && yarn storybook:test:client\"",
|
|
66
|
+
"storybook:test:client": "test-storybook",
|
|
54
67
|
"test": "vitest run",
|
|
55
68
|
"test:update": "vitest run -u",
|
|
56
69
|
"test:watch": "vitest"
|
|
57
70
|
},
|
|
58
71
|
"type": "module",
|
|
59
|
-
"version": "0.0.
|
|
72
|
+
"version": "0.0.3",
|
|
60
73
|
"exports": {
|
|
61
74
|
".": {
|
|
62
75
|
"import": {
|
package/tsconfig.json
CHANGED
package/types/field.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { type Maybe } from '@strictly/base'
|
|
2
2
|
|
|
3
|
-
export enum
|
|
3
|
+
export enum UnreliableFieldConversionType {
|
|
4
4
|
Success = 0,
|
|
5
5
|
Failure = 1,
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
export type UnreliableFieldConversion<V = any, E = any> = {
|
|
9
|
+
type: UnreliableFieldConversionType.Success,
|
|
9
10
|
value: V,
|
|
10
11
|
} | {
|
|
11
|
-
type:
|
|
12
|
+
type: UnreliableFieldConversionType.Failure,
|
|
12
13
|
error: E,
|
|
13
14
|
value: Maybe<V>,
|
|
14
15
|
}
|
|
@@ -18,23 +19,33 @@ export type FieldConversion<V, E> = {
|
|
|
18
19
|
// a `from` type of `string`, and a `to` type of `number`
|
|
19
20
|
|
|
20
21
|
// TODO converter can also have the allowable value path as a parameter
|
|
21
|
-
export type
|
|
22
|
+
export type UnreliableFieldConverter<
|
|
22
23
|
From,
|
|
23
24
|
To,
|
|
24
25
|
E,
|
|
25
26
|
ValuePath extends string,
|
|
26
27
|
Context,
|
|
27
28
|
> = {
|
|
28
|
-
(from: From, valuePath: ValuePath, context: Context):
|
|
29
|
+
(from: From, valuePath: ValuePath, context: Context): UnreliableFieldConversion<To, E>,
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
export type
|
|
32
|
+
export type Annotation = {
|
|
33
|
+
readonly required: boolean,
|
|
34
|
+
readonly readonly: boolean,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
export type AnnotatedFieldConversion<V = any> = {
|
|
39
|
+
value: V,
|
|
40
|
+
} & Annotation
|
|
41
|
+
|
|
42
|
+
export type AnnotatedFieldConverter<
|
|
32
43
|
From,
|
|
33
44
|
To,
|
|
34
45
|
ValuePath extends string,
|
|
35
46
|
Context,
|
|
36
47
|
> = {
|
|
37
|
-
(from: From, valuePath: ValuePath, context: Context): To
|
|
48
|
+
(from: From, valuePath: ValuePath, context: Context): AnnotatedFieldConversion<To>,
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
export type TwoWayFieldConverter<
|
|
@@ -44,9 +55,9 @@ export type TwoWayFieldConverter<
|
|
|
44
55
|
ValuePath extends string,
|
|
45
56
|
Context,
|
|
46
57
|
> = {
|
|
47
|
-
convert:
|
|
58
|
+
convert: AnnotatedFieldConverter<From, To, ValuePath, Context>,
|
|
48
59
|
|
|
49
|
-
revert:
|
|
60
|
+
revert: UnreliableFieldConverter<To, From, E, ValuePath, Context>,
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
export type FieldValueFactory<V, ValuePath extends string, Context> = {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ReadonlyTypeOfType,
|
|
3
|
+
type Type,
|
|
4
|
+
type Validator,
|
|
5
|
+
type ValueOfType,
|
|
6
|
+
} from '@strictly/define'
|
|
7
|
+
import {
|
|
8
|
+
type SimplifyDeep,
|
|
9
|
+
type ValueOf,
|
|
10
|
+
} from 'type-fest'
|
|
11
|
+
import { type Field } from 'types/field'
|
|
12
|
+
|
|
13
|
+
export type FlattenedValidatorsOfFields<
|
|
14
|
+
ValuePathsToTypePaths extends Readonly<Record<string, string>>,
|
|
15
|
+
FlattenedTypeDefs extends Partial<Readonly<Record<ValueOf<ValuePathsToTypePaths>, Type>>>,
|
|
16
|
+
FormFields extends Partial<Readonly<Record<keyof ValuePathsToTypePaths, Field>>>,
|
|
17
|
+
> = SimplifyDeep<{
|
|
18
|
+
readonly [
|
|
19
|
+
K in keyof ValuePathsToTypePaths as FormFields[K] extends Field ? ValuePathsToTypePaths[K] : never
|
|
20
|
+
]: ValidatorOfField<
|
|
21
|
+
NonNullable<FormFields[K]>,
|
|
22
|
+
FlattenedTypeDefs[ValuePathsToTypePaths[K]],
|
|
23
|
+
K
|
|
24
|
+
>
|
|
25
|
+
}>
|
|
26
|
+
|
|
27
|
+
type ValidatorOfField<
|
|
28
|
+
F extends Field,
|
|
29
|
+
T extends Type | undefined,
|
|
30
|
+
ValuePath extends string | number | symbol,
|
|
31
|
+
> = ValuePath extends string ? F extends Field<infer V, infer E> ? undefined extends T ? Validator<V, E, ValuePath>
|
|
32
|
+
: Validator<ValueOfType<ReadonlyTypeOfType<NonNullable<T>>>, E, ValuePath>
|
|
33
|
+
: never
|
|
34
|
+
: never
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
annotations,
|
|
3
|
+
validate,
|
|
4
|
+
type Validator,
|
|
5
|
+
} from '@strictly/define'
|
|
6
|
+
import { type Simplify } from 'type-fest'
|
|
7
|
+
|
|
8
|
+
export type MergedOfValidators<
|
|
9
|
+
Validators1 extends Partial<Readonly<Record<Keys, Validator>>>,
|
|
10
|
+
Validators2 extends Partial<Readonly<Record<Keys, Validator>>>,
|
|
11
|
+
Keys extends string = Extract<keyof Validators1 | keyof Validators2, string>,
|
|
12
|
+
> = Simplify<{
|
|
13
|
+
readonly [K in Keys]: undefined extends Validators1[K] ? undefined extends Validators2[K]
|
|
14
|
+
// validator1 and validator 2 are undefined
|
|
15
|
+
? never
|
|
16
|
+
// validator 1 is undefined
|
|
17
|
+
: Validators2[K]
|
|
18
|
+
: undefined extends Validators2[K]
|
|
19
|
+
// validator 2 is undefined
|
|
20
|
+
? Validators1[K]
|
|
21
|
+
// validator 2 and validator 2 are defined
|
|
22
|
+
: MergedOfValidator<NonNullable<Validators1[K]>, NonNullable<Validators2[K]>>
|
|
23
|
+
}>
|
|
24
|
+
|
|
25
|
+
export type MergedOfValidator<
|
|
26
|
+
Validator1 extends Validator,
|
|
27
|
+
Validator2 extends Validator,
|
|
28
|
+
> = Validator1 extends Validator<infer V, infer E1, infer P, infer C>
|
|
29
|
+
? Validator2 extends Validator<V, infer E2, P, C> ? Validator<V, E1 | E2, P, C>
|
|
30
|
+
: never
|
|
31
|
+
: never
|
|
32
|
+
|
|
33
|
+
export function mergeValidators<
|
|
34
|
+
Validators1 extends Partial<Readonly<Record<Keys, Validator>>>,
|
|
35
|
+
Validators2 extends Partial<Readonly<Record<Keys, Validator>>>,
|
|
36
|
+
Keys extends string = Extract<keyof Validators1 | keyof Validators2, string>,
|
|
37
|
+
>(
|
|
38
|
+
validators1: Validators1,
|
|
39
|
+
validators2: Validators2,
|
|
40
|
+
): MergedOfValidators<Validators1, Validators2, Keys> {
|
|
41
|
+
const validators = {
|
|
42
|
+
...validators1,
|
|
43
|
+
...validators2,
|
|
44
|
+
}
|
|
45
|
+
const keys1 = new Set(Object.keys(validators1))
|
|
46
|
+
const keys2 = new Set(Object.keys(validators2))
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
48
|
+
return Array.from(keys1.intersection(keys2)).reduce(
|
|
49
|
+
function (validators, key) {
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
51
|
+
const validator1 = validators1[key as keyof Validators1]
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
53
|
+
const validator2 = validators2[key as keyof Validators2]
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
56
|
+
validators[key as Keys] = {
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
validate: function (value: any, valuePath: string, context: any) {
|
|
59
|
+
const error = validate(validator1!, value, valuePath, context)
|
|
60
|
+
if (error != null) {
|
|
61
|
+
return error
|
|
62
|
+
}
|
|
63
|
+
return validate(validator2!, value, valuePath, context)
|
|
64
|
+
},
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
annotations: function (valuePath: string, context: any) {
|
|
67
|
+
const annotations1 = annotations(validator1!, valuePath, context)
|
|
68
|
+
const annotations2 = annotations(validator2!, valuePath, context)
|
|
69
|
+
return {
|
|
70
|
+
readonly: annotations1.readonly || annotations2.readonly,
|
|
71
|
+
required: annotations1.required || annotations2.required,
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
} as any
|
|
76
|
+
return validators
|
|
77
|
+
},
|
|
78
|
+
validators,
|
|
79
|
+
) as unknown as MergedOfValidators<Validators1, Validators2, Keys>
|
|
80
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ErrorOfField } from 'types/error_of_field'
|
|
2
2
|
import { type Field } from 'types/field'
|
|
3
3
|
|
|
4
4
|
describe('ErrorTypeOfField', function () {
|
|
5
5
|
it('equals expected type', function () {
|
|
6
6
|
const e = Symbol()
|
|
7
7
|
type E = typeof e
|
|
8
|
-
expectTypeOf<
|
|
8
|
+
expectTypeOf<ErrorOfField<Field<unknown, E>>>().toEqualTypeOf<E>()
|
|
9
9
|
})
|
|
10
10
|
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type booleanType,
|
|
3
|
+
type numberType,
|
|
4
|
+
type Validator,
|
|
5
|
+
} from '@strictly/define'
|
|
6
|
+
import { type Field } from 'types/field'
|
|
7
|
+
import { type FlattenedValidatorsOfFields } from 'types/flattened_validators_of_fields'
|
|
8
|
+
|
|
9
|
+
const error = Symbol()
|
|
10
|
+
type Error = typeof error
|
|
11
|
+
|
|
12
|
+
describe('FlattenedValidatorsOfFields', function () {
|
|
13
|
+
it('maps the converter types', function () {
|
|
14
|
+
type Fields = {
|
|
15
|
+
a: Field<string, Error>,
|
|
16
|
+
}
|
|
17
|
+
type T = FlattenedValidatorsOfFields<
|
|
18
|
+
{
|
|
19
|
+
a: 'b',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
b: typeof numberType,
|
|
23
|
+
},
|
|
24
|
+
Fields
|
|
25
|
+
>
|
|
26
|
+
expectTypeOf<T>().toEqualTypeOf<{
|
|
27
|
+
readonly b: Validator<number, Error, 'a'>,
|
|
28
|
+
}>()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('ignores extraneous types not listed in the fields', function () {
|
|
32
|
+
type FormFields = {
|
|
33
|
+
a: Field<string, Error>,
|
|
34
|
+
}
|
|
35
|
+
type T = FlattenedValidatorsOfFields<
|
|
36
|
+
{
|
|
37
|
+
a: 'b',
|
|
38
|
+
c: 'd',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
b: typeof numberType,
|
|
42
|
+
d: typeof booleanType,
|
|
43
|
+
},
|
|
44
|
+
FormFields
|
|
45
|
+
>
|
|
46
|
+
expectTypeOf<T>().toEqualTypeOf<{
|
|
47
|
+
readonly b: Validator<number, Error, 'a'>,
|
|
48
|
+
}>()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('handles multiple fields', function () {
|
|
52
|
+
type FormFields = {
|
|
53
|
+
a: Field<string, Error>,
|
|
54
|
+
c: Field<boolean, never>,
|
|
55
|
+
}
|
|
56
|
+
type T = FlattenedValidatorsOfFields<
|
|
57
|
+
{
|
|
58
|
+
a: 'b',
|
|
59
|
+
c: 'd',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
b: typeof numberType,
|
|
63
|
+
d: typeof booleanType,
|
|
64
|
+
},
|
|
65
|
+
FormFields
|
|
66
|
+
>
|
|
67
|
+
expectTypeOf<T>().toEqualTypeOf<{
|
|
68
|
+
readonly b: Validator<number, Error, 'a'>,
|
|
69
|
+
readonly d: Validator<boolean, never, 'c'>,
|
|
70
|
+
}>()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('allows synthesized fields', function () {
|
|
74
|
+
type FormFields = {
|
|
75
|
+
a: Field<string, Error>,
|
|
76
|
+
c: Field<number, never>,
|
|
77
|
+
}
|
|
78
|
+
type T = FlattenedValidatorsOfFields<
|
|
79
|
+
{
|
|
80
|
+
a: 'b',
|
|
81
|
+
c: 'd',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
b: typeof numberType,
|
|
85
|
+
},
|
|
86
|
+
FormFields
|
|
87
|
+
>
|
|
88
|
+
expectTypeOf<T>().toEqualTypeOf<{
|
|
89
|
+
readonly b: Validator<number, Error, 'a'>,
|
|
90
|
+
readonly d: Validator<number, never, 'c'>,
|
|
91
|
+
}>()
|
|
92
|
+
})
|
|
93
|
+
})
|