@strictly/react-form 0.0.15 → 0.0.16
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_builder.d.ts +1 -1
- package/.out/core/mobx/form_model.d.ts +3 -4
- package/.out/core/mobx/form_model.js +27 -23
- package/.out/core/mobx/specs/form_model.tests.js +18 -8
- package/.out/core/mobx/types.d.ts +4 -4
- package/.out/index.d.ts +0 -1
- package/.out/index.js +0 -1
- package/.out/mantine/create_field_view.d.ts +20 -0
- package/.out/mantine/create_field_view.js +54 -0
- package/.out/mantine/create_list.js +3 -2
- package/.out/mantine/hooks.d.ts +4 -1
- package/.out/mantine/hooks.js +13 -1
- package/.out/mantine/specs/field_view_hooks.stories.d.ts +12 -0
- package/.out/mantine/specs/field_view_hooks.stories.js +61 -0
- package/.out/mantine/specs/field_view_hooks.tests.d.ts +1 -0
- package/.out/mantine/specs/field_view_hooks.tests.js +12 -0
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/core/mobx/field_adapter_builder.ts +2 -2
- package/core/mobx/form_model.ts +43 -27
- package/core/mobx/specs/form_model.tests.ts +28 -11
- package/core/mobx/types.ts +4 -4
- package/dist/index.cjs +124 -70
- package/dist/index.d.cts +26 -26
- package/dist/index.d.ts +26 -26
- package/dist/index.js +121 -62
- package/index.ts +0 -1
- package/mantine/create_field_view.tsx +94 -0
- package/mantine/create_list.tsx +9 -2
- package/mantine/hooks.tsx +18 -1
- package/mantine/specs/__snapshots__/field_view_hooks.tests.tsx.snap +153 -0
- package/mantine/specs/field_view_hooks.stories.tsx +112 -0
- package/mantine/specs/field_view_hooks.tests.tsx +15 -0
- package/package.json +1 -1
- package/.out/mantine/field_view.d.ts +0 -18
- package/.out/mantine/field_view.js +0 -16
- package/mantine/field_view.tsx +0 -39
package/mantine/hooks.tsx
CHANGED
|
@@ -44,6 +44,10 @@ import {
|
|
|
44
44
|
createCheckbox,
|
|
45
45
|
type SuppliedCheckboxProps,
|
|
46
46
|
} from './create_checkbox'
|
|
47
|
+
import {
|
|
48
|
+
createFieldView,
|
|
49
|
+
type FieldViewProps,
|
|
50
|
+
} from './create_field_view'
|
|
47
51
|
import {
|
|
48
52
|
createFieldsView,
|
|
49
53
|
type FieldsView,
|
|
@@ -93,7 +97,8 @@ export function useMantineFormFields<
|
|
|
93
97
|
onFieldFocus,
|
|
94
98
|
onFieldSubmit,
|
|
95
99
|
fields,
|
|
96
|
-
|
|
100
|
+
// should use FieldView rather than observing fields directly from here
|
|
101
|
+
}: FieldsViewProps<F>): Omit<MantineFormImpl<F>, 'fields'> {
|
|
97
102
|
const form = useMemo(
|
|
98
103
|
function () {
|
|
99
104
|
return new MantineFormImpl(fields)
|
|
@@ -184,6 +189,12 @@ class MantineFormImpl<
|
|
|
184
189
|
> = new Cache(
|
|
185
190
|
createList.bind(this),
|
|
186
191
|
)
|
|
192
|
+
private readonly fieldViewCache: Cache<
|
|
193
|
+
[keyof AllFieldsOfFields<F>],
|
|
194
|
+
ComponentType<FieldViewProps<F, Exclude<keyof AllFieldsOfFields<F>, number | symbol>>>
|
|
195
|
+
> = new Cache(
|
|
196
|
+
createFieldView.bind(this),
|
|
197
|
+
)
|
|
187
198
|
private readonly fieldsViewCache: Cache<
|
|
188
199
|
// the cache cannot reference keys, so we just use any
|
|
189
200
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -392,6 +403,12 @@ class MantineFormImpl<
|
|
|
392
403
|
>
|
|
393
404
|
}
|
|
394
405
|
|
|
406
|
+
fieldView<K extends keyof AllFieldsOfFields<F>>(valuePath: K): ComponentType<FieldViewProps<F, K>> {
|
|
407
|
+
return this.fieldViewCache.retrieveOrCreate(
|
|
408
|
+
valuePath,
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
395
412
|
fieldsView<
|
|
396
413
|
K extends keyof AllFieldsOfFields<F>,
|
|
397
414
|
P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>,
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`field view hooks > renders Empty 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<style
|
|
6
|
+
data-mantine-styles="classes"
|
|
7
|
+
>
|
|
8
|
+
@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}
|
|
9
|
+
</style>
|
|
10
|
+
<div
|
|
11
|
+
class="m_4081bf90 mantine-Group-root"
|
|
12
|
+
style="--group-gap: var(--mantine-spacing-md); --group-align: start; --group-justify: flex-start; --group-wrap: wrap; flex: 1;"
|
|
13
|
+
>
|
|
14
|
+
<div
|
|
15
|
+
class="m_46b77525 mantine-InputWrapper-root mantine-TextInput-root"
|
|
16
|
+
style="flex: 1;"
|
|
17
|
+
>
|
|
18
|
+
<div
|
|
19
|
+
class="m_6c018570 mantine-Input-wrapper mantine-TextInput-wrapper"
|
|
20
|
+
data-variant="default"
|
|
21
|
+
>
|
|
22
|
+
<input
|
|
23
|
+
aria-invalid="false"
|
|
24
|
+
class="m_8fb7ebe7 mantine-Input-input mantine-TextInput-input"
|
|
25
|
+
data-variant="default"
|
|
26
|
+
id="mantine-0px4bipx4"
|
|
27
|
+
value=""
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<button
|
|
32
|
+
class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root"
|
|
33
|
+
style="--button-color: var(--mantine-color-white);"
|
|
34
|
+
type="button"
|
|
35
|
+
>
|
|
36
|
+
<span
|
|
37
|
+
class="m_80f1301b mantine-Button-inner"
|
|
38
|
+
>
|
|
39
|
+
<span
|
|
40
|
+
class="m_811560b9 mantine-Button-label"
|
|
41
|
+
>
|
|
42
|
+
Submit Field
|
|
43
|
+
</span>
|
|
44
|
+
</span>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
exports[`field view hooks > renders Error 1`] = `
|
|
51
|
+
<div>
|
|
52
|
+
<style
|
|
53
|
+
data-mantine-styles="classes"
|
|
54
|
+
>
|
|
55
|
+
@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}
|
|
56
|
+
</style>
|
|
57
|
+
<div
|
|
58
|
+
class="m_4081bf90 mantine-Group-root"
|
|
59
|
+
style="--group-gap: var(--mantine-spacing-md); --group-align: start; --group-justify: flex-start; --group-wrap: wrap; flex: 1;"
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
class="m_46b77525 mantine-InputWrapper-root mantine-TextInput-root"
|
|
63
|
+
data-error="true"
|
|
64
|
+
style="flex: 1;"
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
class="m_6c018570 mantine-Input-wrapper mantine-TextInput-wrapper"
|
|
68
|
+
data-error="true"
|
|
69
|
+
data-variant="default"
|
|
70
|
+
style="--input-margin-bottom: calc(var(--mantine-spacing-xs) / 2);"
|
|
71
|
+
>
|
|
72
|
+
<input
|
|
73
|
+
aria-describedby="mantine-0cyk5rcyk-error"
|
|
74
|
+
aria-invalid="true"
|
|
75
|
+
class="m_8fb7ebe7 mantine-Input-input mantine-TextInput-input"
|
|
76
|
+
data-error="true"
|
|
77
|
+
data-variant="default"
|
|
78
|
+
id="mantine-0cyk5rcyk"
|
|
79
|
+
value="hello"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
<p
|
|
83
|
+
class="m_8f816625 mantine-InputWrapper-error mantine-TextInput-error"
|
|
84
|
+
id="mantine-0cyk5rcyk-error"
|
|
85
|
+
>
|
|
86
|
+
no hellos allowed
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
<button
|
|
90
|
+
class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root"
|
|
91
|
+
style="--button-color: var(--mantine-color-white);"
|
|
92
|
+
type="button"
|
|
93
|
+
>
|
|
94
|
+
<span
|
|
95
|
+
class="m_80f1301b mantine-Button-inner"
|
|
96
|
+
>
|
|
97
|
+
<span
|
|
98
|
+
class="m_811560b9 mantine-Button-label"
|
|
99
|
+
>
|
|
100
|
+
Submit Field
|
|
101
|
+
</span>
|
|
102
|
+
</span>
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
exports[`field view hooks > renders Populated 1`] = `
|
|
109
|
+
<div>
|
|
110
|
+
<style
|
|
111
|
+
data-mantine-styles="classes"
|
|
112
|
+
>
|
|
113
|
+
@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}
|
|
114
|
+
</style>
|
|
115
|
+
<div
|
|
116
|
+
class="m_4081bf90 mantine-Group-root"
|
|
117
|
+
style="--group-gap: var(--mantine-spacing-md); --group-align: start; --group-justify: flex-start; --group-wrap: wrap; flex: 1;"
|
|
118
|
+
>
|
|
119
|
+
<div
|
|
120
|
+
class="m_46b77525 mantine-InputWrapper-root mantine-TextInput-root"
|
|
121
|
+
style="flex: 1;"
|
|
122
|
+
>
|
|
123
|
+
<div
|
|
124
|
+
class="m_6c018570 mantine-Input-wrapper mantine-TextInput-wrapper"
|
|
125
|
+
data-variant="default"
|
|
126
|
+
>
|
|
127
|
+
<input
|
|
128
|
+
aria-invalid="false"
|
|
129
|
+
class="m_8fb7ebe7 mantine-Input-input mantine-TextInput-input"
|
|
130
|
+
data-variant="default"
|
|
131
|
+
id="mantine-0cyk5rcyk"
|
|
132
|
+
value="hello"
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<button
|
|
137
|
+
class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root"
|
|
138
|
+
style="--button-color: var(--mantine-color-white);"
|
|
139
|
+
type="button"
|
|
140
|
+
>
|
|
141
|
+
<span
|
|
142
|
+
class="m_80f1301b mantine-Button-inner"
|
|
143
|
+
>
|
|
144
|
+
<span
|
|
145
|
+
class="m_811560b9 mantine-Button-label"
|
|
146
|
+
>
|
|
147
|
+
Submit Field
|
|
148
|
+
</span>
|
|
149
|
+
</span>
|
|
150
|
+
</button>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
`;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Group,
|
|
4
|
+
TextInput,
|
|
5
|
+
} from '@mantine/core'
|
|
6
|
+
import { action } from '@storybook/addon-actions'
|
|
7
|
+
import {
|
|
8
|
+
type Meta,
|
|
9
|
+
type StoryObj,
|
|
10
|
+
} from '@storybook/react'
|
|
11
|
+
import { type FieldsViewProps } from 'core/props'
|
|
12
|
+
import { useMantineFormFields } from 'mantine/hooks'
|
|
13
|
+
import {
|
|
14
|
+
type ChangeEvent,
|
|
15
|
+
useCallback,
|
|
16
|
+
} from 'react'
|
|
17
|
+
import { type Field } from 'types/field'
|
|
18
|
+
|
|
19
|
+
function Component(props: FieldsViewProps<{
|
|
20
|
+
$: Field<string, string>,
|
|
21
|
+
}>) {
|
|
22
|
+
const form = useMantineFormFields(props)
|
|
23
|
+
const FieldView = form.fieldView('$')
|
|
24
|
+
return (
|
|
25
|
+
<FieldView>
|
|
26
|
+
{({
|
|
27
|
+
error,
|
|
28
|
+
value,
|
|
29
|
+
onBlur,
|
|
30
|
+
onFocus,
|
|
31
|
+
onSubmit,
|
|
32
|
+
onValueChange,
|
|
33
|
+
}) => {
|
|
34
|
+
// this *is* a component eslint
|
|
35
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
36
|
+
const onChange = useCallback(({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
|
|
37
|
+
onValueChange(value)
|
|
38
|
+
}, [onValueChange])
|
|
39
|
+
return (
|
|
40
|
+
<Group
|
|
41
|
+
align='start'
|
|
42
|
+
flex={1}
|
|
43
|
+
>
|
|
44
|
+
<TextInput
|
|
45
|
+
error={error}
|
|
46
|
+
flex={1}
|
|
47
|
+
onBlur={onBlur}
|
|
48
|
+
onChange={onChange}
|
|
49
|
+
onFocus={onFocus}
|
|
50
|
+
value={value}
|
|
51
|
+
/>
|
|
52
|
+
{/* normally this would be done on enter, but with a field view you can implement it however you want */}
|
|
53
|
+
<Button onClick={onSubmit}>
|
|
54
|
+
Submit Field
|
|
55
|
+
</Button>
|
|
56
|
+
</Group>
|
|
57
|
+
)
|
|
58
|
+
}}
|
|
59
|
+
</FieldView>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const meta: Meta<typeof Component> = {
|
|
64
|
+
component: Component,
|
|
65
|
+
args: {
|
|
66
|
+
onFieldBlur: action('onFieldBlur'),
|
|
67
|
+
onFieldFocus: action('onFieldFocus'),
|
|
68
|
+
onFieldSubmit: action('onFieldSubmit'),
|
|
69
|
+
onFieldValueChange: action('onFieldValueChange'),
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default meta
|
|
74
|
+
|
|
75
|
+
type Story = StoryObj<typeof Component>
|
|
76
|
+
|
|
77
|
+
export const Empty: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
fields: {
|
|
80
|
+
$: {
|
|
81
|
+
readonly: false,
|
|
82
|
+
required: false,
|
|
83
|
+
value: '',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const Populated: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
fields: {
|
|
92
|
+
$: {
|
|
93
|
+
readonly: false,
|
|
94
|
+
required: false,
|
|
95
|
+
value: 'hello',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const Error: Story = {
|
|
102
|
+
args: {
|
|
103
|
+
fields: {
|
|
104
|
+
$: {
|
|
105
|
+
readonly: false,
|
|
106
|
+
required: false,
|
|
107
|
+
value: 'hello',
|
|
108
|
+
error: 'no hellos allowed',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { composeStories } from '@storybook/react'
|
|
2
|
+
import { toArray } from '@strictly/base'
|
|
3
|
+
import {
|
|
4
|
+
render,
|
|
5
|
+
} from '@testing-library/react'
|
|
6
|
+
import * as stories from './field_view_hooks.stories'
|
|
7
|
+
|
|
8
|
+
const composedStories = composeStories(stories)
|
|
9
|
+
|
|
10
|
+
describe('field view hooks', function () {
|
|
11
|
+
it.each(toArray(composedStories))('renders %s', function (_name, Story) {
|
|
12
|
+
const wrapper = render(<Story />)
|
|
13
|
+
expect(wrapper.container).toMatchSnapshot()
|
|
14
|
+
})
|
|
15
|
+
})
|
package/package.json
CHANGED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { type ComponentType } from 'react';
|
|
2
|
-
import { type ErrorOfField } from 'types/error_of_field';
|
|
3
|
-
import { type Fields } from 'types/field';
|
|
4
|
-
import { type ValueTypeOfField } from 'types/value_type_of_field';
|
|
5
|
-
/**
|
|
6
|
-
* Displays current value and error of a field
|
|
7
|
-
*/
|
|
8
|
-
export declare function FieldView<F extends Fields, K extends keyof F>({ fields, valuePath, children, }: {
|
|
9
|
-
fields: F;
|
|
10
|
-
valuePath: K;
|
|
11
|
-
children: (props: {
|
|
12
|
-
value: ValueTypeOfField<F[K]>;
|
|
13
|
-
error: ErrorOfField<F[K]> | undefined;
|
|
14
|
-
ErrorSink: ComponentType<{
|
|
15
|
-
error: ErrorOfField<F[K]>;
|
|
16
|
-
}>;
|
|
17
|
-
}) => JSX.Element;
|
|
18
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Observer } from 'mobx-react';
|
|
3
|
-
import { Empty } from 'util/empty';
|
|
4
|
-
/**
|
|
5
|
-
* Displays current value and error of a field
|
|
6
|
-
*/
|
|
7
|
-
export function FieldView({ fields, valuePath, children, }) {
|
|
8
|
-
return (_jsx(Observer, { children: () => {
|
|
9
|
-
const { value, error, } = fields[valuePath];
|
|
10
|
-
return children({
|
|
11
|
-
value,
|
|
12
|
-
error,
|
|
13
|
-
ErrorSink: Empty,
|
|
14
|
-
});
|
|
15
|
-
} }));
|
|
16
|
-
}
|
package/mantine/field_view.tsx
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { Observer } from 'mobx-react'
|
|
2
|
-
import { type ComponentType } from 'react'
|
|
3
|
-
import { type ErrorOfField } from 'types/error_of_field'
|
|
4
|
-
import { type Fields } from 'types/field'
|
|
5
|
-
import { type ValueTypeOfField } from 'types/value_type_of_field'
|
|
6
|
-
import { Empty } from 'util/empty'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Displays current value and error of a field
|
|
10
|
-
*/
|
|
11
|
-
export function FieldView<F extends Fields, K extends keyof F>({
|
|
12
|
-
fields,
|
|
13
|
-
valuePath,
|
|
14
|
-
children,
|
|
15
|
-
}: {
|
|
16
|
-
fields: F,
|
|
17
|
-
valuePath: K,
|
|
18
|
-
children: (props: {
|
|
19
|
-
value: ValueTypeOfField<F[K]>,
|
|
20
|
-
error: ErrorOfField<F[K]> | undefined,
|
|
21
|
-
ErrorSink: ComponentType<{ error: ErrorOfField<F[K]> }>,
|
|
22
|
-
}) => JSX.Element,
|
|
23
|
-
}) {
|
|
24
|
-
return (
|
|
25
|
-
<Observer>
|
|
26
|
-
{() => {
|
|
27
|
-
const {
|
|
28
|
-
value,
|
|
29
|
-
error,
|
|
30
|
-
} = fields[valuePath]
|
|
31
|
-
return children({
|
|
32
|
-
value,
|
|
33
|
-
error,
|
|
34
|
-
ErrorSink: Empty,
|
|
35
|
-
})
|
|
36
|
-
}}
|
|
37
|
-
</Observer>
|
|
38
|
-
)
|
|
39
|
-
}
|