mayoiga 0.1.6 → 0.1.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/.github/workflows/checker.yml +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +16 -3
- package/dist/index.test.js +70 -1
- package/package.json +23 -18
- package/src/index.test.tsx +98 -1
- package/src/index.ts +53 -31
- package/tsconfig.json +0 -2
- package/yarn.lock +1281 -849
package/dist/index.d.ts
CHANGED
|
@@ -101,14 +101,18 @@ export type Controller<State extends StateRestriction> = {
|
|
|
101
101
|
};
|
|
102
102
|
type FormHook<State extends StateRestriction, Schema extends ZodType<any, any, any>> = {
|
|
103
103
|
controller: Controller<State>;
|
|
104
|
-
|
|
104
|
+
/**
|
|
105
|
+
* @param handler handling logic
|
|
106
|
+
* @returns created handler is always return Promise, because of the schema validation run it asynchronously.
|
|
107
|
+
*/
|
|
108
|
+
handleSubmit: <R>(handler: (e: BaseSyntheticEvent) => (val: {
|
|
105
109
|
success: true;
|
|
106
110
|
data: zodInfer<Schema>;
|
|
107
111
|
error: undefined;
|
|
108
112
|
} | {
|
|
109
113
|
success: false;
|
|
110
114
|
error: ReadonlyArray<ZodIssue>;
|
|
111
|
-
}) =>
|
|
115
|
+
}) => R) => (e: BaseSyntheticEvent) => Promise<R>;
|
|
112
116
|
api: Subscriber<FullFormState<State>, FormControllerBehavior<State>>;
|
|
113
117
|
} & Controller<State>;
|
|
114
118
|
/**
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,8 @@ function createFormStore(initialState, schema) {
|
|
|
54
54
|
},
|
|
55
55
|
pushFormErrors: (validator) => (prev) => {
|
|
56
56
|
const pushedErrors = validator(prev.value);
|
|
57
|
-
|
|
57
|
+
const allErrors = { ...prev.errors, ...pushedErrors };
|
|
58
|
+
return { ...prev, isValid: Object.keys(allErrors).length === 0, errors: allErrors };
|
|
58
59
|
},
|
|
59
60
|
reduceFormErrors: (reducer) => (prev) => {
|
|
60
61
|
return { ...prev, errors: reducer(prev.errors) };
|
|
@@ -98,8 +99,20 @@ export function createFormHook(initialState, schema) {
|
|
|
98
99
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
99
100
|
return eventHandled({ success: true, data: result.data, error: undefined });
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
const handled = eventHandled({ success: false, error: result.error.issues });
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
104
|
+
if (typeof handled['then'] === 'function') {
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
106
|
+
return handled.then((x) => {
|
|
107
|
+
store.actions.handleIssues(result.error.issues);
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
109
|
+
return x;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
store.actions.handleIssues(result.error.issues);
|
|
114
|
+
return handled;
|
|
115
|
+
}
|
|
103
116
|
});
|
|
104
117
|
};
|
|
105
118
|
},
|
package/dist/index.test.js
CHANGED
|
@@ -53,11 +53,15 @@ test('zod parse value before submit', async () => {
|
|
|
53
53
|
else {
|
|
54
54
|
expect('should not called').toBe(result.error);
|
|
55
55
|
}
|
|
56
|
+
return Promise.resolve('returns value');
|
|
56
57
|
});
|
|
57
58
|
const Top = () => {
|
|
58
59
|
return (React.createElement("div", null,
|
|
59
60
|
React.createElement(AgeInputComponent, { controller: TestFormHook }),
|
|
60
|
-
React.createElement("button", { "data-testid": "submit-button", onClick:
|
|
61
|
+
React.createElement("button", { "data-testid": "submit-button", onClick: async (e) => {
|
|
62
|
+
const returnedValue = await handleSubmitTester(e);
|
|
63
|
+
expect(returnedValue).toBe('returns value');
|
|
64
|
+
} })));
|
|
61
65
|
};
|
|
62
66
|
render(React.createElement(Top, null));
|
|
63
67
|
const ageInput = screen.getByTestId('age-input');
|
|
@@ -76,6 +80,49 @@ test('zod parse value before submit', async () => {
|
|
|
76
80
|
});
|
|
77
81
|
});
|
|
78
82
|
});
|
|
83
|
+
test('able to handle parse error on submit', async () => {
|
|
84
|
+
const TestFormHook = createTestHook();
|
|
85
|
+
const INPUT_AGE = 'aaaa';
|
|
86
|
+
const EXPECTED_ISSUE = {
|
|
87
|
+
code: 'custom',
|
|
88
|
+
message: 'Invalid input',
|
|
89
|
+
path: ['age'],
|
|
90
|
+
};
|
|
91
|
+
const handleSubmitTester = TestFormHook.handleSubmit((e) => (result) => {
|
|
92
|
+
if (result.success) {
|
|
93
|
+
expect('should not succeed').toBe(false);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
expect(result.success).toBe(false);
|
|
97
|
+
expect(result.error).toStrictEqual([EXPECTED_ISSUE]);
|
|
98
|
+
}
|
|
99
|
+
return Promise.resolve('returns value');
|
|
100
|
+
});
|
|
101
|
+
const Top = () => {
|
|
102
|
+
return (React.createElement("div", null,
|
|
103
|
+
React.createElement(AgeInputComponent, { controller: TestFormHook }),
|
|
104
|
+
React.createElement("button", { "data-testid": "submit-button", onClick: async (e) => {
|
|
105
|
+
const returnedValue = await handleSubmitTester(e);
|
|
106
|
+
expect(returnedValue).toBe('returns value');
|
|
107
|
+
} })));
|
|
108
|
+
};
|
|
109
|
+
render(React.createElement(Top, null));
|
|
110
|
+
const ageInput = screen.getByTestId('age-input');
|
|
111
|
+
await userEvent.type(ageInput, INPUT_AGE);
|
|
112
|
+
await waitFor(() => {
|
|
113
|
+
const ageInput = screen.getByTestId('age-input');
|
|
114
|
+
expect(ageInput.value).toBe(INPUT_AGE);
|
|
115
|
+
});
|
|
116
|
+
const submitButton = screen.getByTestId('submit-button');
|
|
117
|
+
await userEvent.click(submitButton);
|
|
118
|
+
await waitFor(() => {
|
|
119
|
+
console.log('waiting');
|
|
120
|
+
TestFormHook.api.actions.peek((s) => {
|
|
121
|
+
expect(s.value.age).toBe(INPUT_AGE);
|
|
122
|
+
expect(s.errors.age).toBe(EXPECTED_ISSUE.message);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
79
126
|
test('User is able to re-initialize the form state', async () => {
|
|
80
127
|
const TestFormHook = createTestHook();
|
|
81
128
|
const Top = () => {
|
|
@@ -89,3 +136,25 @@ test('User is able to re-initialize the form state', async () => {
|
|
|
89
136
|
TestFormHook.actions.initializeForm({}, { cleanup: true });
|
|
90
137
|
expect(TestFormHook.api.getState().isDirty).toBeFalsy();
|
|
91
138
|
});
|
|
139
|
+
test('User is able to push server side validation', async () => {
|
|
140
|
+
const TestFormHook = createTestHook();
|
|
141
|
+
const Top = () => {
|
|
142
|
+
return (React.createElement("div", null,
|
|
143
|
+
React.createElement(AgeInputComponent, { controller: TestFormHook.controller })));
|
|
144
|
+
};
|
|
145
|
+
render(React.createElement(Top, null));
|
|
146
|
+
const ageInput = screen.getByTestId('age-input');
|
|
147
|
+
await userEvent.type(ageInput, '9999');
|
|
148
|
+
TestFormHook.actions.pushFormErrors((_s) => ({ age: 'server side error' }));
|
|
149
|
+
const state1 = TestFormHook.api.getState();
|
|
150
|
+
expect(state1.isDirty).toBeTruthy();
|
|
151
|
+
expect(state1.isValid).toBeFalsy();
|
|
152
|
+
expect(state1.errors.age).toEqual('server side error');
|
|
153
|
+
await userEvent.type(ageInput, 'hgoehoge');
|
|
154
|
+
// confirm empty field won't overwrite any messages
|
|
155
|
+
TestFormHook.actions.pushFormErrors((_s) => ({}));
|
|
156
|
+
const state2 = TestFormHook.api.getState();
|
|
157
|
+
expect(state2.isDirty).toBeTruthy();
|
|
158
|
+
expect(state2.isValid).toBeFalsy();
|
|
159
|
+
expect(state2.errors.age).toEqual('Invalid input');
|
|
160
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mayoiga",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Type safe hook based form library for React",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,27 +43,32 @@
|
|
|
43
43
|
"zod": "^3.20.2"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"nozuchi": "^0.0.
|
|
46
|
+
"nozuchi": "^0.0.6"
|
|
47
|
+
},
|
|
48
|
+
"resolutions": {
|
|
49
|
+
"react": "^18.2.0",
|
|
50
|
+
"react-dom": "^18.2.0",
|
|
51
|
+
"@types/react": "^18.2.57"
|
|
47
52
|
},
|
|
48
53
|
"devDependencies": {
|
|
49
|
-
"@testing-library/react": "^14.
|
|
50
|
-
"@testing-library/user-event": "^14.
|
|
51
|
-
"@types/node": "^
|
|
52
|
-
"@types/react": "^18.2.
|
|
53
|
-
"@types/use-sync-external-store": "^0.0.
|
|
54
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
55
|
-
"@typescript-eslint/parser": "^
|
|
56
|
-
"eslint": "^8.
|
|
57
|
-
"eslint-plugin-import": "^2.
|
|
58
|
-
"eslint-plugin-testing-library": "^
|
|
59
|
-
"jsdom": "^
|
|
60
|
-
"prettier": "^2.
|
|
54
|
+
"@testing-library/react": "^14.2.1",
|
|
55
|
+
"@testing-library/user-event": "^14.5.2",
|
|
56
|
+
"@types/node": "^20.11.19",
|
|
57
|
+
"@types/react": "^18.2.57",
|
|
58
|
+
"@types/use-sync-external-store": "^0.0.6",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
|
60
|
+
"@typescript-eslint/parser": "^7.0.2",
|
|
61
|
+
"eslint": "^8.56.0",
|
|
62
|
+
"eslint-plugin-import": "^2.29.1",
|
|
63
|
+
"eslint-plugin-testing-library": "^6.2.0",
|
|
64
|
+
"jsdom": "^24.0.0",
|
|
65
|
+
"prettier": "^3.2.5",
|
|
61
66
|
"react": "^18.2.0",
|
|
62
67
|
"react-dom": "^18.2.0",
|
|
63
|
-
"typedoc": "^0.
|
|
64
|
-
"typescript": "^5.
|
|
68
|
+
"typedoc": "^0.25.8",
|
|
69
|
+
"typescript": "^5.3.3",
|
|
65
70
|
"use-sync-external-store": "^1.2.0",
|
|
66
|
-
"vitest": "^
|
|
67
|
-
"zod": "^3.
|
|
71
|
+
"vitest": "^1.3.0",
|
|
72
|
+
"zod": "^3.22.4"
|
|
68
73
|
}
|
|
69
74
|
}
|
package/src/index.test.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {render, screen, waitFor} from '@testing-library/react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
|
+
|
|
3
4
|
import React from 'react';
|
|
4
5
|
|
|
5
6
|
import {test, expect} from 'vitest';
|
|
@@ -89,13 +90,20 @@ test('zod parse value before submit', async () => {
|
|
|
89
90
|
} else {
|
|
90
91
|
expect('should not called').toBe(result.error);
|
|
91
92
|
}
|
|
93
|
+
return Promise.resolve('returns value');
|
|
92
94
|
});
|
|
93
95
|
|
|
94
96
|
const Top = () => {
|
|
95
97
|
return (
|
|
96
98
|
<div>
|
|
97
99
|
<AgeInputComponent controller={TestFormHook} />
|
|
98
|
-
<button
|
|
100
|
+
<button
|
|
101
|
+
data-testid="submit-button"
|
|
102
|
+
onClick={async (e) => {
|
|
103
|
+
const returnedValue = await handleSubmitTester(e);
|
|
104
|
+
expect(returnedValue).toBe('returns value');
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
99
107
|
</div>
|
|
100
108
|
);
|
|
101
109
|
};
|
|
@@ -122,6 +130,63 @@ test('zod parse value before submit', async () => {
|
|
|
122
130
|
});
|
|
123
131
|
});
|
|
124
132
|
|
|
133
|
+
test('able to handle parse error on submit', async () => {
|
|
134
|
+
const TestFormHook = createTestHook();
|
|
135
|
+
|
|
136
|
+
const INPUT_AGE = 'aaaa';
|
|
137
|
+
const EXPECTED_ISSUE = {
|
|
138
|
+
code: 'custom',
|
|
139
|
+
message: 'Invalid input',
|
|
140
|
+
path: ['age'],
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const handleSubmitTester = TestFormHook.handleSubmit((e) => (result) => {
|
|
144
|
+
if (result.success) {
|
|
145
|
+
expect('should not succeed').toBe(false);
|
|
146
|
+
} else {
|
|
147
|
+
expect(result.success).toBe(false);
|
|
148
|
+
expect(result.error).toStrictEqual([EXPECTED_ISSUE]);
|
|
149
|
+
}
|
|
150
|
+
return Promise.resolve('returns value');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const Top = () => {
|
|
154
|
+
return (
|
|
155
|
+
<div>
|
|
156
|
+
<AgeInputComponent controller={TestFormHook} />
|
|
157
|
+
<button
|
|
158
|
+
data-testid="submit-button"
|
|
159
|
+
onClick={async (e) => {
|
|
160
|
+
const returnedValue = await handleSubmitTester(e);
|
|
161
|
+
expect(returnedValue).toBe('returns value');
|
|
162
|
+
}}
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
render(<Top />);
|
|
169
|
+
|
|
170
|
+
const ageInput: HTMLInputElement = screen.getByTestId('age-input');
|
|
171
|
+
await userEvent.type(ageInput, INPUT_AGE);
|
|
172
|
+
|
|
173
|
+
await waitFor(() => {
|
|
174
|
+
const ageInput: HTMLInputElement = screen.getByTestId('age-input');
|
|
175
|
+
expect(ageInput.value).toBe(INPUT_AGE);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const submitButton = screen.getByTestId('submit-button');
|
|
179
|
+
await userEvent.click(submitButton);
|
|
180
|
+
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
console.log('waiting');
|
|
183
|
+
TestFormHook.api.actions.peek((s) => {
|
|
184
|
+
expect(s.value.age).toBe(INPUT_AGE);
|
|
185
|
+
expect(s.errors.age).toBe(EXPECTED_ISSUE.message);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
125
190
|
test('User is able to re-initialize the form state', async () => {
|
|
126
191
|
const TestFormHook = createTestHook();
|
|
127
192
|
|
|
@@ -142,3 +207,35 @@ test('User is able to re-initialize the form state', async () => {
|
|
|
142
207
|
TestFormHook.actions.initializeForm({}, {cleanup: true});
|
|
143
208
|
expect(TestFormHook.api.getState().isDirty).toBeFalsy();
|
|
144
209
|
});
|
|
210
|
+
|
|
211
|
+
test('User is able to push server side validation', async () => {
|
|
212
|
+
const TestFormHook = createTestHook();
|
|
213
|
+
|
|
214
|
+
const Top = () => {
|
|
215
|
+
return (
|
|
216
|
+
<div>
|
|
217
|
+
<AgeInputComponent controller={TestFormHook.controller} />
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
render(<Top />);
|
|
223
|
+
|
|
224
|
+
const ageInput: HTMLInputElement = screen.getByTestId('age-input');
|
|
225
|
+
await userEvent.type(ageInput, '9999');
|
|
226
|
+
|
|
227
|
+
TestFormHook.actions.pushFormErrors((_s) => ({age: 'server side error'}));
|
|
228
|
+
const state1 = TestFormHook.api.getState();
|
|
229
|
+
expect(state1.isDirty).toBeTruthy();
|
|
230
|
+
expect(state1.isValid).toBeFalsy();
|
|
231
|
+
expect(state1.errors.age).toEqual('server side error');
|
|
232
|
+
|
|
233
|
+
await userEvent.type(ageInput, 'hgoehoge');
|
|
234
|
+
|
|
235
|
+
// confirm empty field won't overwrite any messages
|
|
236
|
+
TestFormHook.actions.pushFormErrors((_s) => ({}));
|
|
237
|
+
const state2 = TestFormHook.api.getState();
|
|
238
|
+
expect(state2.isDirty).toBeTruthy();
|
|
239
|
+
expect(state2.isValid).toBeFalsy();
|
|
240
|
+
expect(state2.errors.age).toEqual('Invalid input');
|
|
241
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -120,10 +120,13 @@ function createFormStore<StateBeforeValidation extends StateRestriction, Schema
|
|
|
120
120
|
type StateKeys = keyof StateBeforeValidation;
|
|
121
121
|
|
|
122
122
|
const initialErrors = Object.freeze(
|
|
123
|
-
Object.keys(initialState).reduce(
|
|
124
|
-
buf
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
Object.keys(initialState).reduce(
|
|
124
|
+
(buf, k) => {
|
|
125
|
+
buf[k as StateKeys] = null;
|
|
126
|
+
return buf;
|
|
127
|
+
},
|
|
128
|
+
{} as any as Err,
|
|
129
|
+
),
|
|
127
130
|
);
|
|
128
131
|
|
|
129
132
|
const fullInitialState: FullState = Object.freeze({
|
|
@@ -137,15 +140,18 @@ function createFormStore<StateBeforeValidation extends StateRestriction, Schema
|
|
|
137
140
|
if (result.success) {
|
|
138
141
|
return {...prev, isDirty: true, isValid: true, value: newValue, errors: initialErrors};
|
|
139
142
|
}
|
|
140
|
-
const newErrors = result.error.issues.reduce(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
const newErrors = result.error.issues.reduce(
|
|
144
|
+
(buf, iss) => {
|
|
145
|
+
// TODO: should support nested value?
|
|
146
|
+
const shallowPath = iss.path[0];
|
|
147
|
+
if (shallowPath === undefined) {
|
|
148
|
+
return buf;
|
|
149
|
+
}
|
|
150
|
+
buf[shallowPath as any as StateKeys] = iss.message;
|
|
144
151
|
return buf;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}, {} as any as Err);
|
|
152
|
+
},
|
|
153
|
+
{} as any as Err,
|
|
154
|
+
);
|
|
149
155
|
|
|
150
156
|
return {...prev, isDirty: true, isValid: false, errors: newErrors, value: newValue};
|
|
151
157
|
};
|
|
@@ -165,20 +171,24 @@ function createFormStore<StateBeforeValidation extends StateRestriction, Schema
|
|
|
165
171
|
}),
|
|
166
172
|
handleIssues: (issues) => (prev) => {
|
|
167
173
|
// FIXME: handle duplication
|
|
168
|
-
const newErrors = issues.reduce(
|
|
169
|
-
|
|
170
|
-
|
|
174
|
+
const newErrors = issues.reduce(
|
|
175
|
+
(buf, iss) => {
|
|
176
|
+
const shallowPath = iss.path[0];
|
|
177
|
+
if (shallowPath === undefined) {
|
|
178
|
+
return buf;
|
|
179
|
+
}
|
|
180
|
+
buf[shallowPath as any as StateKeys] = iss.message;
|
|
171
181
|
return buf;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}, {} as any as Err);
|
|
182
|
+
},
|
|
183
|
+
{} as any as Err,
|
|
184
|
+
);
|
|
176
185
|
|
|
177
186
|
return {...prev, isDirty: true, isValid: false, errors: newErrors};
|
|
178
187
|
},
|
|
179
188
|
pushFormErrors: (validator) => (prev) => {
|
|
180
189
|
const pushedErrors = validator(prev.value);
|
|
181
|
-
|
|
190
|
+
const allErrors = {...prev.errors, ...pushedErrors};
|
|
191
|
+
return {...prev, isValid: Object.keys(allErrors).length === 0, errors: allErrors};
|
|
182
192
|
},
|
|
183
193
|
reduceFormErrors: (reducer) => (prev) => {
|
|
184
194
|
return {...prev, errors: reducer(prev.errors)};
|
|
@@ -239,13 +249,15 @@ export type Controller<State extends StateRestriction> = {
|
|
|
239
249
|
type FormHook<State extends StateRestriction, Schema extends ZodType<any, any, any>> = {
|
|
240
250
|
controller: Controller<State>;
|
|
241
251
|
|
|
242
|
-
|
|
252
|
+
/**
|
|
253
|
+
* @param handler handling logic
|
|
254
|
+
* @returns created handler is always return Promise, because of the schema validation run it asynchronously.
|
|
255
|
+
*/
|
|
256
|
+
handleSubmit: <R>(
|
|
243
257
|
handler: (
|
|
244
258
|
e: BaseSyntheticEvent,
|
|
245
|
-
) => (
|
|
246
|
-
|
|
247
|
-
) => void | Promise<void>,
|
|
248
|
-
) => (e: BaseSyntheticEvent) => void;
|
|
259
|
+
) => (val: {success: true; data: zodInfer<Schema>; error: undefined} | {success: false; error: ReadonlyArray<ZodIssue>}) => R,
|
|
260
|
+
) => (e: BaseSyntheticEvent) => Promise<R>;
|
|
249
261
|
|
|
250
262
|
api: Subscriber<FullFormState<State>, FormControllerBehavior<State>>;
|
|
251
263
|
} & Controller<State>;
|
|
@@ -266,12 +278,10 @@ export function createFormHook<StateBeforeValidation extends StateRestriction, S
|
|
|
266
278
|
}, []);
|
|
267
279
|
},
|
|
268
280
|
useSelector: store.useSelector,
|
|
269
|
-
handleSubmit: (
|
|
281
|
+
handleSubmit: <R>(
|
|
270
282
|
handler: (
|
|
271
283
|
e: BaseSyntheticEvent,
|
|
272
|
-
) => (
|
|
273
|
-
val: {success: true; data: zodInfer<Schema>; error: undefined} | {success: false; error: ReadonlyArray<ZodIssue>},
|
|
274
|
-
) => void | Promise<void>,
|
|
284
|
+
) => (val: {success: true; data: zodInfer<Schema>; error: undefined} | {success: false; error: ReadonlyArray<ZodIssue>}) => R,
|
|
275
285
|
) => {
|
|
276
286
|
return (e: BaseSyntheticEvent) => {
|
|
277
287
|
const eventHandled = handler(e);
|
|
@@ -282,8 +292,20 @@ export function createFormHook<StateBeforeValidation extends StateRestriction, S
|
|
|
282
292
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
283
293
|
return eventHandled({success: true, data: result.data, error: undefined});
|
|
284
294
|
}
|
|
285
|
-
|
|
286
|
-
|
|
295
|
+
|
|
296
|
+
const handled: any = eventHandled({success: false, error: result.error.issues});
|
|
297
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
298
|
+
if (typeof handled['then'] === 'function') {
|
|
299
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
300
|
+
return handled.then((x: any) => {
|
|
301
|
+
store.actions.handleIssues(result.error.issues);
|
|
302
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
303
|
+
return x;
|
|
304
|
+
}) as R;
|
|
305
|
+
} else {
|
|
306
|
+
store.actions.handleIssues(result.error.issues);
|
|
307
|
+
return handled as R;
|
|
308
|
+
}
|
|
287
309
|
});
|
|
288
310
|
};
|
|
289
311
|
},
|