@strictly/react-form 0.0.6 → 0.0.8
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/hooks.d.ts +5 -1
- package/.out/core/mobx/hooks.js +13 -3
- package/.out/core/mobx/specs/form_presenter.tests.js +3 -6
- package/.out/core/mobx/specs/{merge_field_adapters_with_two_way_converter.js → merge_field_adapters_with_two_way_converter.tests.js} +15 -16
- package/.out/core/mobx/specs/sub_form_field_adapters.tests.js +127 -12
- package/.out/core/mobx/sub_form_field_adapters.d.ts +6 -4
- package/.out/core/mobx/sub_form_field_adapters.js +28 -4
- package/.out/field_converters/nullable_to_boolean_converter.d.ts +2 -2
- package/.out/mantine/create_checkbox.js +1 -0
- package/.out/mantine/create_fields_view.d.ts +1 -1
- package/.out/mantine/create_fields_view.js +4 -4
- package/.out/mantine/create_form.d.ts +1 -1
- package/.out/mantine/create_list.d.ts +1 -1
- package/.out/mantine/create_pill.d.ts +1 -1
- package/.out/mantine/create_radio.d.ts +1 -1
- package/.out/mantine/create_radio_group.js +1 -0
- package/.out/mantine/create_text_input.js +7 -2
- package/.out/mantine/create_value_input.js +1 -0
- package/.out/mantine/error_renderer.d.ts +1 -1
- package/.out/mantine/error_renderer.js +1 -1
- package/.out/mantine/hooks.d.ts +9 -9
- package/.out/mantine/specs/checkbox_hooks.stories.d.ts +2 -6
- package/.out/mantine/specs/checkbox_hooks.stories.js +4 -16
- package/.out/mantine/specs/fields_view_hooks.stories.d.ts +1 -1
- package/.out/mantine/specs/fields_view_hooks.stories.js +6 -3
- package/.out/mantine/specs/form_hooks.stories.d.ts +2 -2
- package/.out/mantine/specs/form_hooks.stories.js +4 -1
- package/.out/mantine/specs/radio_group_hooks.stories.d.ts +2 -6
- package/.out/mantine/specs/radio_group_hooks.stories.js +5 -17
- package/.out/mantine/specs/select_hooks.stories.d.ts +2 -6
- package/.out/mantine/specs/select_hooks.stories.js +4 -16
- package/.out/mantine/specs/text_input_hooks.stories.d.ts +2 -5
- package/.out/mantine/specs/text_input_hooks.stories.js +5 -5
- package/.out/mantine/specs/value_input_hooks.stories.d.ts +2 -5
- package/.out/mantine/specs/value_input_hooks.stories.js +5 -5
- package/.out/mantine/types.d.ts +4 -2
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/core/mobx/hooks.ts +24 -6
- package/core/mobx/specs/form_presenter.tests.ts +6 -6
- package/core/mobx/specs/{merge_field_adapters_with_two_way_converter.ts → merge_field_adapters_with_two_way_converter.tests.ts} +16 -16
- package/core/mobx/specs/sub_form_field_adapters.tests.ts +193 -17
- package/core/mobx/sub_form_field_adapters.ts +74 -11
- package/dist/index.cjs +77 -32
- package/dist/index.d.cts +25 -18
- package/dist/index.d.ts +25 -18
- package/dist/index.js +68 -21
- package/field_converters/nullable_to_boolean_converter.ts +2 -3
- package/mantine/create_checkbox.tsx +2 -1
- package/mantine/create_fields_view.tsx +17 -14
- package/mantine/create_form.tsx +2 -2
- package/mantine/create_list.tsx +1 -1
- package/mantine/create_pill.tsx +1 -1
- package/mantine/create_radio.tsx +1 -1
- package/mantine/create_radio_group.tsx +6 -2
- package/mantine/create_text_input.tsx +9 -3
- package/mantine/create_value_input.tsx +2 -1
- package/mantine/error_renderer.ts +1 -1
- package/mantine/hooks.tsx +19 -14
- package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +1 -64
- package/mantine/specs/__snapshots__/fields_view_hooks.tests.tsx.snap +52 -52
- package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +1 -179
- package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +1 -83
- package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +27 -27
- package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +31 -31
- package/mantine/specs/checkbox_hooks.stories.tsx +5 -21
- package/mantine/specs/fields_view_hooks.stories.tsx +16 -4
- package/mantine/specs/form_hooks.stories.tsx +10 -3
- package/mantine/specs/radio_group_hooks.stories.tsx +6 -20
- package/mantine/specs/select_hooks.stories.tsx +5 -21
- package/mantine/specs/text_input_hooks.stories.tsx +5 -8
- package/mantine/specs/value_input_hooks.stories.tsx +5 -8
- package/mantine/types.ts +7 -3
- package/package.json +2 -1
- /package/.out/core/mobx/specs/{merge_field_adapters_with_two_way_converter.d.ts → merge_field_adapters_with_two_way_converter.tests.d.ts} +0 -0
|
@@ -4,7 +4,11 @@ import { type FormPresenter } from './form_presenter';
|
|
|
4
4
|
import { type ValuePathsOfPresenter } from './types';
|
|
5
5
|
type ValueOfPresenter<P extends FormPresenter<any, any, any, any>> = P extends FormPresenter<infer T, any, any, any> ? ValueOfType<ReadonlyTypeOfType<T>> : never;
|
|
6
6
|
type ModelOfPresenter<P extends FormPresenter<any, any, any, any>> = ReturnType<P['createModel']>;
|
|
7
|
-
export declare function useDefaultMobxFormHooks<P extends FormPresenter<any, any, any, any>>(presenter: P, value: ValueOfPresenter<P>,
|
|
7
|
+
export declare function useDefaultMobxFormHooks<P extends FormPresenter<any, any, any, any>>(presenter: P, value: ValueOfPresenter<P>, { onValidFieldSubmit, onValidFormSubmit, }: {
|
|
8
|
+
onValidFieldSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void;
|
|
9
|
+
onValidFormSubmit?: (model: ModelOfPresenter<P>, value: ValueOfPresenter<P>) => void;
|
|
10
|
+
}): {
|
|
8
11
|
model: ModelOfPresenter<P>;
|
|
12
|
+
onFormSubmit?: (value: ValueOfPresenter<P>) => void;
|
|
9
13
|
} & Omit<FieldsViewProps<ModelOfPresenter<P>['fields']>, 'fields'>;
|
|
10
14
|
export {};
|
package/.out/core/mobx/hooks.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useMemo, } from 'react';
|
|
2
2
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
|
-
export function useDefaultMobxFormHooks(presenter, value,
|
|
3
|
+
export function useDefaultMobxFormHooks(presenter, value, { onValidFieldSubmit, onValidFormSubmit, }) {
|
|
4
4
|
const model = useMemo(function () {
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
6
6
|
return presenter.createModel(value);
|
|
@@ -17,13 +17,13 @@ export function useDefaultMobxFormHooks(presenter, value, onValidSubmit) {
|
|
|
17
17
|
]);
|
|
18
18
|
const onFieldSubmit = useCallback(function (valuePath) {
|
|
19
19
|
if (presenter.validateField(model, valuePath)) {
|
|
20
|
-
|
|
20
|
+
onValidFieldSubmit?.(model, valuePath);
|
|
21
21
|
}
|
|
22
22
|
return false;
|
|
23
23
|
}, [
|
|
24
24
|
presenter,
|
|
25
25
|
model,
|
|
26
|
-
|
|
26
|
+
onValidFieldSubmit,
|
|
27
27
|
]);
|
|
28
28
|
const onFieldBlur = useCallback(function (path) {
|
|
29
29
|
// work around potential loss of focus prior to state potentially invalidating change triggering
|
|
@@ -38,10 +38,20 @@ export function useDefaultMobxFormHooks(presenter, value, onValidSubmit) {
|
|
|
38
38
|
presenter,
|
|
39
39
|
model,
|
|
40
40
|
]);
|
|
41
|
+
const onFormSubmit = useCallback(function () {
|
|
42
|
+
if (presenter.validateAll(model)) {
|
|
43
|
+
onValidFormSubmit?.(model, model.value);
|
|
44
|
+
}
|
|
45
|
+
}, [
|
|
46
|
+
presenter,
|
|
47
|
+
model,
|
|
48
|
+
onValidFormSubmit,
|
|
49
|
+
]);
|
|
41
50
|
return {
|
|
42
51
|
model,
|
|
43
52
|
onFieldValueChange,
|
|
44
53
|
onFieldSubmit,
|
|
45
54
|
onFieldBlur,
|
|
55
|
+
onFormSubmit,
|
|
46
56
|
};
|
|
47
57
|
}
|
|
@@ -21,18 +21,16 @@ describe('all', function () {
|
|
|
21
21
|
describe('FlattenedTypePathsToConvertersOf', function () {
|
|
22
22
|
describe('record', function () {
|
|
23
23
|
const typeDef = record(numberType);
|
|
24
|
-
let t;
|
|
25
24
|
it('equals expected type', function () {
|
|
26
|
-
expectTypeOf(
|
|
25
|
+
expectTypeOf().toEqualTypeOf();
|
|
27
26
|
});
|
|
28
27
|
});
|
|
29
28
|
describe('object', function () {
|
|
30
29
|
const typeDef = object()
|
|
31
30
|
.field('x', stringType)
|
|
32
31
|
.field('y', booleanType);
|
|
33
|
-
let t;
|
|
34
32
|
it('equals expected type', function () {
|
|
35
|
-
expectTypeOf(
|
|
33
|
+
expectTypeOf().toEqualTypeOf();
|
|
36
34
|
});
|
|
37
35
|
it('matches representative adapters', function () {
|
|
38
36
|
expectTypeOf().toMatchTypeOf();
|
|
@@ -50,9 +48,8 @@ describe('all', function () {
|
|
|
50
48
|
'$.b': '$.y',
|
|
51
49
|
'$.c': '$.z',
|
|
52
50
|
};
|
|
53
|
-
let t;
|
|
54
51
|
it('equals expected type', function () {
|
|
55
|
-
expectTypeOf(
|
|
52
|
+
expectTypeOf().toEqualTypeOf();
|
|
56
53
|
});
|
|
57
54
|
});
|
|
58
55
|
});
|
|
@@ -9,9 +9,8 @@ const error3 = Symbol();
|
|
|
9
9
|
const error4 = Symbol();
|
|
10
10
|
const context = Symbol();
|
|
11
11
|
describe('MergedOfFieldAdapterWithTwoWayConverter', function () {
|
|
12
|
-
let m;
|
|
13
12
|
it('merges the errors', function () {
|
|
14
|
-
expectTypeOf().toEqualTypeOf(
|
|
13
|
+
expectTypeOf().toEqualTypeOf();
|
|
15
14
|
});
|
|
16
15
|
});
|
|
17
16
|
const originalIntegerAdapter = identityAdapter(0);
|
|
@@ -69,20 +68,20 @@ describe('mergeFieldAdaptersWithTwoWayConverter', function () {
|
|
|
69
68
|
beforeEach(function () {
|
|
70
69
|
result = merged.booleanAdapter.revert(true, 'booleanAdapter', context);
|
|
71
70
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
71
|
+
it('returns the same value on revert', function () {
|
|
72
|
+
expect(result).toEqual(expect.objectContaining({
|
|
73
|
+
value: true,
|
|
74
|
+
type: UnreliableFieldConversionType.Success,
|
|
75
|
+
}));
|
|
76
|
+
});
|
|
77
|
+
it('calls the mocked converter', function () {
|
|
78
|
+
expect(converter.revert).toHaveBeenCalledOnce();
|
|
79
|
+
expect(converter.revert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
|
|
80
|
+
});
|
|
81
|
+
it('calls the mocked adapter', function () {
|
|
82
|
+
expect(booleanAdapter.revert).toHaveBeenCalledOnce();
|
|
83
|
+
expect(booleanAdapter.revert).toHaveBeenCalledWith(true, 'booleanAdapter', context);
|
|
84
|
+
});
|
|
86
85
|
});
|
|
87
86
|
});
|
|
88
87
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { list, numberType, object, stringType, } from '@strictly/define';
|
|
1
2
|
import { subFormFieldAdapters, } from 'core/mobx/sub_form_field_adapters';
|
|
2
|
-
import {
|
|
3
|
+
import { UnreliableFieldConversionType } from 'types/field_converters';
|
|
4
|
+
import { mockDeep, mockReset, } from 'vitest-mock-extended';
|
|
3
5
|
describe('subFormFieldAdapters', () => {
|
|
4
|
-
const fieldAdapter1 = mockDeep();
|
|
5
|
-
const fieldAdapter2 = mockDeep();
|
|
6
6
|
describe('empty value', () => {
|
|
7
|
-
const adapters = subFormFieldAdapters({}, '$.a');
|
|
7
|
+
const adapters = subFormFieldAdapters({}, '$.a', stringType);
|
|
8
8
|
it('equals expected type', () => {
|
|
9
9
|
expectTypeOf(adapters).toEqualTypeOf();
|
|
10
10
|
});
|
|
@@ -13,28 +13,143 @@ describe('subFormFieldAdapters', () => {
|
|
|
13
13
|
});
|
|
14
14
|
});
|
|
15
15
|
describe('single adapter', () => {
|
|
16
|
-
const
|
|
16
|
+
const mockedFieldAdapter1 = mockDeep();
|
|
17
|
+
const fieldAdapter1 = mockedFieldAdapter1;
|
|
18
|
+
const type = object().field('a', stringType);
|
|
19
|
+
const subAdapters = {
|
|
17
20
|
$: fieldAdapter1,
|
|
18
|
-
}
|
|
21
|
+
};
|
|
22
|
+
const adapters = subFormFieldAdapters(subAdapters, '$.a', type);
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
mockReset(mockedFieldAdapter1);
|
|
25
|
+
});
|
|
19
26
|
it('equals expected type', () => {
|
|
20
|
-
|
|
27
|
+
// TODO toEqualTypeOf (cannot reason about revert optionality, seems to be a TS issue as they
|
|
28
|
+
// are both optional AFAICT)
|
|
29
|
+
expectTypeOf(adapters).toMatchTypeOf();
|
|
21
30
|
});
|
|
22
31
|
it('equals expected value', () => {
|
|
23
|
-
expect(adapters).toEqual({ '$.a':
|
|
32
|
+
expect(adapters).toEqual({ '$.a': expect.anything() });
|
|
33
|
+
});
|
|
34
|
+
it('calls convert with the correct paths and values', () => {
|
|
35
|
+
const mockedReturnedValue = {
|
|
36
|
+
value: false,
|
|
37
|
+
required: false,
|
|
38
|
+
readonly: false,
|
|
39
|
+
};
|
|
40
|
+
mockedFieldAdapter1.convert.mockReturnValue(mockedReturnedValue);
|
|
41
|
+
const returnedValue = adapters['$.a'].convert('x', '$.a', { a: 'y' });
|
|
42
|
+
expect(fieldAdapter1.convert).toHaveBeenCalledWith('x', '$', 'y');
|
|
43
|
+
expect(returnedValue).toEqual(mockedReturnedValue);
|
|
44
|
+
});
|
|
45
|
+
it('calls revert with the correct paths and values', () => {
|
|
46
|
+
const mockedReturnedValue = {
|
|
47
|
+
type: UnreliableFieldConversionType.Success,
|
|
48
|
+
value: 'ok',
|
|
49
|
+
};
|
|
50
|
+
mockedFieldAdapter1.revert.mockReturnValue(mockedReturnedValue);
|
|
51
|
+
const returnedValue = adapters['$.a'].revert?.(true, '$.a', { a: 'y' });
|
|
52
|
+
expect(fieldAdapter1.revert).toHaveBeenCalledWith(true, '$', 'y');
|
|
53
|
+
expect(returnedValue).toEqual(mockedReturnedValue);
|
|
54
|
+
});
|
|
55
|
+
it('calls create with the correct paths and values', () => {
|
|
56
|
+
const mockedReturnedValue = 'x';
|
|
57
|
+
mockedFieldAdapter1.create.mockReturnValue(mockedReturnedValue);
|
|
58
|
+
const returnedValue = adapters['$.a'].create('$.a', { a: 'y' });
|
|
59
|
+
expect(fieldAdapter1.create).toHaveBeenCalledWith('$', 'y');
|
|
60
|
+
expect(returnedValue).toEqual(mockedReturnedValue);
|
|
24
61
|
});
|
|
25
62
|
});
|
|
26
63
|
describe('multiple adapters', () => {
|
|
64
|
+
const mockedFieldAdapter1 = mockDeep();
|
|
65
|
+
const fieldAdapter1 = mockedFieldAdapter1;
|
|
66
|
+
const mockedFieldAdapter2 = mockDeep();
|
|
67
|
+
const fieldAdapter2 = mockedFieldAdapter2;
|
|
68
|
+
const type = object()
|
|
69
|
+
.field('a', object().field('x', stringType).field('y', numberType));
|
|
27
70
|
const adapters = subFormFieldAdapters({
|
|
28
71
|
'$.x': fieldAdapter1,
|
|
29
72
|
'$.y': fieldAdapter2,
|
|
30
|
-
}, '$.a');
|
|
73
|
+
}, '$.a', type);
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
mockReset(mockedFieldAdapter1);
|
|
76
|
+
mockReset(mockedFieldAdapter2);
|
|
77
|
+
});
|
|
31
78
|
it('equals expected type', () => {
|
|
32
|
-
|
|
79
|
+
// TODO toEqualTypeOf (seems to be a TS error)
|
|
80
|
+
expectTypeOf(adapters).toMatchTypeOf();
|
|
81
|
+
});
|
|
82
|
+
it('equals expected value', () => {
|
|
83
|
+
expect(adapters).toEqual({
|
|
84
|
+
'$.a.x': expect.anything(),
|
|
85
|
+
'$.a.y': expect.anything(),
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('calls convert with correct paths and values', () => {
|
|
89
|
+
const subContext = {
|
|
90
|
+
x: 'a',
|
|
91
|
+
y: 1,
|
|
92
|
+
};
|
|
93
|
+
const context = {
|
|
94
|
+
a: subContext,
|
|
95
|
+
};
|
|
96
|
+
it('calls $.a.x', () => {
|
|
97
|
+
const mockedReturnedValue = {
|
|
98
|
+
value: true,
|
|
99
|
+
readonly: true,
|
|
100
|
+
required: false,
|
|
101
|
+
};
|
|
102
|
+
mockedFieldAdapter1.convert.mockReturnValue(mockedReturnedValue);
|
|
103
|
+
const returnedValue = adapters['$.a.x'].convert('b', '$.a.x', context);
|
|
104
|
+
expect(fieldAdapter1.convert).toHaveBeenCalledWith('b', '$.x', subContext);
|
|
105
|
+
expect(returnedValue).toEqual(mockedReturnedValue);
|
|
106
|
+
});
|
|
107
|
+
it('calls $.a.y', () => {
|
|
108
|
+
const mockedReturnedValue = {
|
|
109
|
+
value: false,
|
|
110
|
+
readonly: false,
|
|
111
|
+
required: false,
|
|
112
|
+
};
|
|
113
|
+
mockedFieldAdapter2.convert.mockReturnValue(mockedReturnedValue);
|
|
114
|
+
const returnedValue = adapters['$.a.y'].convert(2, '$.a.y', context);
|
|
115
|
+
expect(fieldAdapter2.convert).toHaveBeenCalledWith(2, '$.y', subContext);
|
|
116
|
+
expect(returnedValue).toEqual(mockedReturnedValue);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('list adapter', () => {
|
|
121
|
+
const mockedFieldAdapter1 = mockDeep();
|
|
122
|
+
const fieldAdapter1 = mockedFieldAdapter1;
|
|
123
|
+
const type = list(stringType);
|
|
124
|
+
const subAdapters = {
|
|
125
|
+
$: fieldAdapter1,
|
|
126
|
+
};
|
|
127
|
+
const adapters = subFormFieldAdapters(subAdapters, '$.*', type);
|
|
128
|
+
beforeEach(() => {
|
|
129
|
+
mockReset(mockedFieldAdapter1);
|
|
130
|
+
});
|
|
131
|
+
it('equals expected type', () => {
|
|
132
|
+
// TODO toEqualTypeOf (seems to be a TS error)
|
|
133
|
+
expectTypeOf(adapters).toMatchTypeOf();
|
|
33
134
|
});
|
|
34
135
|
it('equals expected value', () => {
|
|
35
136
|
expect(adapters).toEqual({
|
|
36
|
-
'
|
|
37
|
-
|
|
137
|
+
'$.*': expect.anything(),
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe('calls convert with correct paths and values', () => {
|
|
141
|
+
const subContext = 'a';
|
|
142
|
+
const context = [subContext];
|
|
143
|
+
it('calls $.*', () => {
|
|
144
|
+
const mockedReturnedValue = {
|
|
145
|
+
value: false,
|
|
146
|
+
readonly: false,
|
|
147
|
+
required: false,
|
|
148
|
+
};
|
|
149
|
+
mockedFieldAdapter1.convert.mockReturnValue(mockedReturnedValue);
|
|
150
|
+
const returnedValue = adapters['$.*'].convert('b', '$.0', context);
|
|
151
|
+
expect(fieldAdapter1.convert).toHaveBeenCalledWith('b', '$', subContext);
|
|
152
|
+
expect(returnedValue).toEqual(mockedReturnedValue);
|
|
38
153
|
});
|
|
39
154
|
});
|
|
40
155
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type StringConcatOf } from '@strictly/base';
|
|
2
|
-
import { type
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { type Type, type ValueOfType } from '@strictly/define';
|
|
3
|
+
import { type ErrorOfFieldAdapter, type FieldAdapter, type FromOfFieldAdapter, type ToOfFieldAdapter, type ValuePathOfFieldAdapter } from './field_adapter';
|
|
4
|
+
type SubFormFieldAdapter<F extends FieldAdapter, ValuePath extends string, Context> = FieldAdapter<FromOfFieldAdapter<F>, ToOfFieldAdapter<F>, ErrorOfFieldAdapter<F>, ValuePathOfFieldAdapter<F> extends StringConcatOf<'$', infer ValuePathSuffix> ? `${ValuePath}${ValuePathSuffix}` : string, Context>;
|
|
5
|
+
type SubFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, TypePath extends string, ValuePath extends string, Context> = {
|
|
6
|
+
[K in keyof SubAdapters as K extends StringConcatOf<'$', infer TypePathSuffix> ? `${TypePath}${TypePathSuffix}` : never]: SubFormFieldAdapter<SubAdapters[K], ValuePath, Context>;
|
|
5
7
|
};
|
|
6
|
-
export declare function subFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>,
|
|
8
|
+
export declare function subFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, TypePath extends string, TypePathsToValuePaths extends Record<TypePath, string>, ContextType extends Type>(subAdapters: SubAdapters, parentTypePath: TypePath, contextType: ContextType): SubFormFieldAdapters<SubAdapters, TypePath, TypePathsToValuePaths[TypePath], ValueOfType<ContextType>>;
|
|
7
9
|
export {};
|
|
@@ -1,8 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
import { flattenValuesOfType, } from '@strictly/define';
|
|
2
|
+
export function subFormFieldAdapters(subAdapters, parentTypePath, contextType) {
|
|
3
|
+
// assume the number of '.' in the type path will correspond to the number of '.' in the value path
|
|
4
|
+
const dotCount = parentTypePath.split('.').length;
|
|
5
|
+
function getSubValuePathAndContext(valuePath, context) {
|
|
6
|
+
const parentValuePath = valuePath.split('.').slice(0, dotCount).join('.');
|
|
7
|
+
const subValuePath = valuePath.replace(parentValuePath, '$');
|
|
8
|
+
const subContext = flattenValuesOfType(contextType, context)[parentValuePath];
|
|
9
|
+
return [
|
|
10
|
+
subValuePath,
|
|
11
|
+
subContext,
|
|
12
|
+
];
|
|
13
|
+
}
|
|
2
14
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
3
|
-
return Object.entries(subAdapters).reduce((acc, [
|
|
4
|
-
const
|
|
5
|
-
|
|
15
|
+
return Object.entries(subAdapters).reduce((acc, [subTypePath, subAdapter,]) => {
|
|
16
|
+
const typePath = subTypePath.replace('$', parentTypePath);
|
|
17
|
+
// adapt field adapter with new path and context
|
|
18
|
+
const adaptedAdapter = {
|
|
19
|
+
convert: (from, valuePath, context) => {
|
|
20
|
+
return subAdapter.convert(from, ...getSubValuePathAndContext(valuePath, context));
|
|
21
|
+
},
|
|
22
|
+
create: (valuePath, context) => {
|
|
23
|
+
return subAdapter.create(...getSubValuePathAndContext(valuePath, context));
|
|
24
|
+
},
|
|
25
|
+
revert: subAdapter.revert && ((from, valuePath, context) => {
|
|
26
|
+
return subAdapter.revert(from, ...getSubValuePathAndContext(valuePath, context));
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
acc[typePath] = adaptedAdapter;
|
|
6
30
|
return acc;
|
|
7
31
|
}, {});
|
|
8
32
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type ReadonlyTypeOfType, type Type, type ValueOfType } from '@strictly/define';
|
|
2
2
|
import { type AnnotatedFieldConversion, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion } from 'types/field_converters';
|
|
3
|
-
export declare class NullableToBooleanConverter<T extends Type,
|
|
3
|
+
export declare class NullableToBooleanConverter<T extends Type, ValuePath extends string, Context, NullType extends null | undefined> implements TwoWayFieldConverterWithValueFactory<ValueOfType<ReadonlyTypeOfType<T>> | NullType, boolean, never, ValuePath, Context> {
|
|
4
4
|
private readonly typeDef;
|
|
5
5
|
private readonly prototype;
|
|
6
6
|
private readonly nullType;
|
|
7
7
|
readonly defaultValue: ValueOfType<ReadonlyTypeOfType<T>> | NullType;
|
|
8
8
|
constructor(typeDef: T, prototype: ValueOfType<ReadonlyTypeOfType<T>>, nullType: NullType, defaultToNull?: boolean);
|
|
9
9
|
convert(from: ValueOfType<ReadonlyTypeOfType<T>> | NullType): AnnotatedFieldConversion<boolean>;
|
|
10
|
-
revert(from: boolean): UnreliableFieldConversion<ValueOfType<ReadonlyTypeOfType<T>> | NullType,
|
|
10
|
+
revert(from: boolean): UnreliableFieldConversion<ValueOfType<ReadonlyTypeOfType<T>> | NullType, never>;
|
|
11
11
|
create(): ValueOfType<ReadonlyTypeOfType<T>> | NullType;
|
|
12
12
|
}
|
|
@@ -4,4 +4,4 @@ import type { AllFieldsOfFields } from 'types/all_fields_of_fields';
|
|
|
4
4
|
import type { Fields } from 'types/field';
|
|
5
5
|
import type { SubFormFields } from 'types/sub_form_fields';
|
|
6
6
|
import type { MantineFieldComponent } from './types';
|
|
7
|
-
export declare function createFieldsView<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FieldsViewProps<P['fields']>, P>;
|
|
7
|
+
export declare function createFieldsView<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FieldsViewProps<P['fields']>, P, never>;
|
|
@@ -31,9 +31,9 @@ export function createFieldsView(valuePath, FieldsView, observableProps) {
|
|
|
31
31
|
acc[toSubKey(fieldKey)] = fieldValue;
|
|
32
32
|
}
|
|
33
33
|
return acc;
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
},
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
36
|
+
{});
|
|
37
|
+
return (_jsx(FieldsView, { ...props, fields: subFields, onFieldBlur: onFieldBlur, onFieldFocus: onFieldFocus, onFieldSubmit: onFieldSubmit, onFieldValueChange: onFieldValueChange }));
|
|
38
38
|
});
|
|
39
39
|
}
|
|
@@ -4,4 +4,4 @@ import { type AllFieldsOfFields } from 'types/all_fields_of_fields';
|
|
|
4
4
|
import { type Fields } from 'types/field';
|
|
5
5
|
import { type ValueTypeOfField } from 'types/value_type_of_field';
|
|
6
6
|
import { type MantineFieldComponent } from './types';
|
|
7
|
-
export declare function createForm<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P>;
|
|
7
|
+
export declare function createForm<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P, never>;
|
|
@@ -10,7 +10,7 @@ export type SuppliedListProps<Value = any, ListPath extends string = string> = {
|
|
|
10
10
|
};
|
|
11
11
|
export declare function createList<F extends Fields, K extends keyof ListFieldsOfFields<F>, Props extends SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>> & {
|
|
12
12
|
children: (valuePath: `${K}.${number}`, value: ElementOfArray<ValueTypeOfField<F[K]>>, index: number) => React.ReactNode;
|
|
13
|
-
}>(this: MantineForm<F>, valuePath: K, List: ComponentType<Props>): MantineFieldComponent<SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>>, Props>;
|
|
13
|
+
}>(this: MantineForm<F>, valuePath: K, List: ComponentType<Props>): MantineFieldComponent<SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>>, Props, never>;
|
|
14
14
|
export declare function DefaultList<Value, ListPath extends string>({ values, listPath, children, }: SuppliedListProps<Value, ListPath> & {
|
|
15
15
|
children: (valuePath: `${ListPath}.${number}`, value: Value, index: number) => React.ReactNode;
|
|
16
16
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,4 +4,4 @@ import { type AllFieldsOfFields } from 'types/all_fields_of_fields';
|
|
|
4
4
|
import { type Fields } from 'types/field';
|
|
5
5
|
import { type MantineFieldComponent, type MantineForm } from './types';
|
|
6
6
|
export type SuppliedPillProps = Pick<PillProps, 'children' | 'disabled'>;
|
|
7
|
-
export declare function createPill<F extends Fields, K extends keyof AllFieldsOfFields<F>, Props extends SuppliedPillProps>(this: MantineForm<F>, valuePath: K, Pill: ComponentType<Props>): MantineFieldComponent<SuppliedPillProps, Props>;
|
|
7
|
+
export declare function createPill<F extends Fields, K extends keyof AllFieldsOfFields<F>, Props extends SuppliedPillProps>(this: MantineForm<F>, valuePath: K, Pill: ComponentType<Props>): MantineFieldComponent<SuppliedPillProps, Props, never>;
|
|
@@ -5,4 +5,4 @@ import { type StringFieldsOfFields } from 'types/string_fields_of_fields';
|
|
|
5
5
|
import { type ValueTypeOfField } from 'types/value_type_of_field';
|
|
6
6
|
import { type MantineFieldComponent, type MantineForm } from './types';
|
|
7
7
|
export type SuppliedRadioProps = Pick<RadioProps, 'value' | 'disabled'>;
|
|
8
|
-
export declare function createRadio<F extends Fields, K extends keyof StringFieldsOfFields<F>, Props extends SuppliedRadioProps>(this: MantineForm<F>, valuePath: K, value: ValueTypeOfField<F[K]>, Radio: ComponentType<Props>): MantineFieldComponent<SuppliedRadioProps, Props>;
|
|
8
|
+
export declare function createRadio<F extends Fields, K extends keyof StringFieldsOfFields<F>, Props extends SuppliedRadioProps>(this: MantineForm<F>, valuePath: K, value: ValueTypeOfField<F[K]>, Radio: ComponentType<Props>): MantineFieldComponent<SuppliedRadioProps, Props, never>;
|
|
@@ -19,9 +19,13 @@ export function createTextInput(valuePath, TextInput) {
|
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
const propSource = ({ ErrorRenderer = DefaultErrorRenderer, }) => {
|
|
22
|
-
const { readonly, required, value, error,
|
|
23
22
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
24
|
-
|
|
23
|
+
const field = this.fields[valuePath];
|
|
24
|
+
if (field == null) {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
26
|
+
throw new Error(`invalid field ${valuePath}`);
|
|
27
|
+
}
|
|
28
|
+
const { readonly, required, value, error, } = field;
|
|
25
29
|
return {
|
|
26
30
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
27
31
|
name: valuePath,
|
|
@@ -35,5 +39,6 @@ export function createTextInput(valuePath, TextInput) {
|
|
|
35
39
|
onKeyUp,
|
|
36
40
|
};
|
|
37
41
|
};
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
38
43
|
return createUnsafePartialObserverComponent(TextInput, propSource, ['ErrorRenderer']);
|
|
39
44
|
}
|
|
@@ -3,4 +3,4 @@ export type ErrorRendererProps<E> = {
|
|
|
3
3
|
error: E;
|
|
4
4
|
};
|
|
5
5
|
export type ErrorRenderer<E = any> = ComponentType<ErrorRendererProps<E>>;
|
|
6
|
-
export declare function DefaultErrorRenderer({ error, }: ErrorRendererProps<any>):
|
|
6
|
+
export declare function DefaultErrorRenderer({ error, }: ErrorRendererProps<any>): string;
|
package/.out/mantine/hooks.d.ts
CHANGED
|
@@ -38,20 +38,20 @@ declare class MantineFormImpl<F extends Fields> implements MantineForm<F> {
|
|
|
38
38
|
onFieldBlur: ((this: void, key: keyof F) => void) | undefined;
|
|
39
39
|
onFieldSubmit: ((this: void, key: keyof F) => boolean | void) | undefined;
|
|
40
40
|
constructor(fields: F);
|
|
41
|
-
textInput<K extends keyof StringFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, TextInputProps
|
|
42
|
-
textInput<K extends keyof StringFieldsOfFields<F>, P extends SuppliedTextInputProps<any>>(valuePath: K, TextInput?: ComponentType<P>): MantineFieldComponent<SuppliedTextInputProps, P
|
|
41
|
+
textInput<K extends keyof StringFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, TextInputProps, ErrorOfField<F[K]>>;
|
|
42
|
+
textInput<K extends keyof StringFieldsOfFields<F>, P extends SuppliedTextInputProps<any>>(valuePath: K, TextInput?: ComponentType<P>): MantineFieldComponent<SuppliedTextInputProps, P, ErrorOfField<F[K]>>;
|
|
43
43
|
valueInput<K extends keyof AllFieldsOfFields<F>, P extends SuppliedValueInputProps<ValueTypeOfField<F[K]>, any>>(valuePath: K, ValueInput: ComponentType<P>): MantineFieldComponent<SuppliedValueInputProps<ValueTypeOfField<F[K]>>, P, ErrorOfField<F[K]>>;
|
|
44
44
|
select<K extends keyof StringFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, ComponentProps<typeof SimpleSelect>, ErrorOfField<F[K]>>;
|
|
45
|
-
checkbox<K extends keyof BooleanFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedCheckboxProps, CheckboxProps
|
|
45
|
+
checkbox<K extends keyof BooleanFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedCheckboxProps, CheckboxProps, ErrorOfField<F[K]>>;
|
|
46
46
|
checkbox<K extends keyof BooleanFieldsOfFields<F>, P extends SuppliedCheckboxProps>(valuePath: K, Checkbox: ComponentType<P>): MantineFieldComponent<SuppliedCheckboxProps, P, ErrorOfField<F[K]>>;
|
|
47
|
-
radioGroup<K extends keyof StringFieldsOfFields<F
|
|
48
|
-
radioGroup<K extends keyof StringFieldsOfFields<F>, P extends SuppliedRadioGroupProps>(valuePath: K, RadioGroup: ComponentType<P>): MantineFieldComponent<SuppliedRadioGroupProps, P
|
|
47
|
+
radioGroup<K extends keyof StringFieldsOfFields<F>, P extends RadioGroupProps = RadioGroupProps>(valuePath: K): MantineFieldComponent<SuppliedRadioGroupProps, P, ErrorOfField<F[K]>>;
|
|
48
|
+
radioGroup<K extends keyof StringFieldsOfFields<F>, P extends SuppliedRadioGroupProps>(valuePath: K, RadioGroup: ComponentType<P>): MantineFieldComponent<SuppliedRadioGroupProps, P, ErrorOfField<F[K]>>;
|
|
49
49
|
radio<K extends keyof StringFieldsOfFields<F>>(valuePath: K, value: ValueTypeOfField<F[K]>): MantineFieldComponent<SuppliedRadioProps, RadioProps, ErrorOfField<F[K]>>;
|
|
50
50
|
radio<K extends keyof StringFieldsOfFields<F>, P extends SuppliedRadioProps>(valuePath: K, value: ValueTypeOfField<F[K]>, Radio: ComponentType<P>): MantineFieldComponent<SuppliedRadioProps, P, ErrorOfField<F[K]>>;
|
|
51
51
|
pill<K extends keyof AllFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedPillProps, PillProps, ErrorOfField<F[K]>>;
|
|
52
|
-
pill<K extends keyof AllFieldsOfFields<F>, P extends SuppliedPillProps>(valuePath: K, Pill: ComponentType<P>): MantineFieldComponent<SuppliedPillProps, P
|
|
53
|
-
list<K extends keyof ListFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedListProps<`${K}.${number}`>, ComponentProps<typeof DefaultList<ElementOfArray<F[K]['value']>, K
|
|
54
|
-
fieldsView<K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>): MantineFieldComponent<FieldsViewProps<P['fields']>, P>;
|
|
55
|
-
form<K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P>;
|
|
52
|
+
pill<K extends keyof AllFieldsOfFields<F>, P extends SuppliedPillProps>(valuePath: K, Pill: ComponentType<P>): MantineFieldComponent<SuppliedPillProps, P, ErrorOfField<F[K]>>;
|
|
53
|
+
list<K extends keyof ListFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedListProps<`${K}.${number}`>, ComponentProps<typeof DefaultList<ElementOfArray<F[K]['value']>, K>>, never>;
|
|
54
|
+
fieldsView<K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>): MantineFieldComponent<FieldsViewProps<P['fields']>, P, never>;
|
|
55
|
+
form<K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P, never>;
|
|
56
56
|
}
|
|
57
57
|
export {};
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
2
2
|
import { type FieldsViewProps } from 'core/props';
|
|
3
|
-
import { type ErrorRenderer } from 'mantine/error_renderer';
|
|
4
3
|
import { type Field } from 'types/field';
|
|
5
|
-
declare function Component({
|
|
4
|
+
declare function Component({ ...props }: FieldsViewProps<{
|
|
6
5
|
$: Field<boolean, string>;
|
|
7
|
-
}>
|
|
8
|
-
ErrorRenderer?: ErrorRenderer;
|
|
9
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
}>): import("react/jsx-runtime").JSX.Element;
|
|
10
7
|
declare const meta: Meta<typeof Component>;
|
|
11
8
|
export default meta;
|
|
12
9
|
type Story = StoryObj<typeof Component>;
|
|
@@ -15,4 +12,3 @@ export declare const On: Story;
|
|
|
15
12
|
export declare const Required: Story;
|
|
16
13
|
export declare const Disabled: Story;
|
|
17
14
|
export declare const Error: Story;
|
|
18
|
-
export declare const CustomError: Story;
|
|
@@ -2,7 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { action } from '@storybook/addon-actions';
|
|
3
3
|
import { useMantineFormFields } from 'mantine/hooks';
|
|
4
4
|
import { CHECKBOX_LABEL } from './checkbox_constants';
|
|
5
|
-
function
|
|
5
|
+
function ErrorRenderer({ error }) {
|
|
6
|
+
return `Error ${error}`;
|
|
7
|
+
}
|
|
8
|
+
function Component({ ...props }) {
|
|
6
9
|
const inputProps = useMantineFormFields(props);
|
|
7
10
|
const CheckboxComponent = inputProps.checkbox('$');
|
|
8
11
|
return (_jsx(CheckboxComponent, { ErrorRenderer: ErrorRenderer, label: CHECKBOX_LABEL }));
|
|
@@ -73,18 +76,3 @@ export const Error = {
|
|
|
73
76
|
},
|
|
74
77
|
},
|
|
75
78
|
};
|
|
76
|
-
export const CustomError = {
|
|
77
|
-
args: {
|
|
78
|
-
fields: {
|
|
79
|
-
$: {
|
|
80
|
-
readonly: false,
|
|
81
|
-
required: false,
|
|
82
|
-
value: true,
|
|
83
|
-
error: 'error',
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
ErrorRenderer: function () {
|
|
87
|
-
return 'custom error';
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
};
|
|
@@ -3,16 +3,19 @@ import { Button, Stack, } from '@mantine/core';
|
|
|
3
3
|
import { action } from '@storybook/addon-actions';
|
|
4
4
|
import { useMantineFormFields } from 'mantine/hooks';
|
|
5
5
|
const onClick = action('some button clicked');
|
|
6
|
+
function ErrorRenderer({ error }) {
|
|
7
|
+
return `error ${error}`;
|
|
8
|
+
}
|
|
6
9
|
function SubFieldsView(props) {
|
|
7
10
|
const form = useMantineFormFields(props);
|
|
8
11
|
const TextInput = form.textInput('$');
|
|
9
|
-
return (_jsxs(Stack, { children: [_jsx(TextInput, { label: 'sub fields view' }), _jsx(Button, { onClick: props.onClick, children: "Bonus Button" })] }));
|
|
12
|
+
return (_jsxs(Stack, { children: [_jsx(TextInput, { ErrorRenderer: ErrorRenderer, label: 'sub fields view' }), _jsx(Button, { onClick: props.onClick, children: "Bonus Button" })] }));
|
|
10
13
|
}
|
|
11
14
|
function Component(props) {
|
|
12
15
|
const form = useMantineFormFields(props);
|
|
13
16
|
const FieldsView = form.fieldsView('$.a', SubFieldsView);
|
|
14
17
|
const TextInput = form.textInput('$');
|
|
15
|
-
return (_jsxs(Stack, { children: [_jsx(TextInput, { label: 'fields view' }), _jsx(FieldsView, { onClick: onClick })] }));
|
|
18
|
+
return (_jsxs(Stack, { children: [_jsx(TextInput, { ErrorRenderer: ErrorRenderer, label: 'fields view' }), _jsx(FieldsView, { onClick: onClick })] }));
|
|
16
19
|
}
|
|
17
20
|
const meta = {
|
|
18
21
|
component: Component,
|
|
@@ -88,7 +91,7 @@ export const Disabled = {
|
|
|
88
91
|
},
|
|
89
92
|
},
|
|
90
93
|
};
|
|
91
|
-
export const
|
|
94
|
+
export const Errors = {
|
|
92
95
|
args: {
|
|
93
96
|
fields: {
|
|
94
97
|
$: {
|