@sqrzro/ui 4.0.0-alpha.54 → 4.0.0-alpha.55
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/dist/forms/components/Form/index.d.ts +2 -1
- package/dist/forms/components/Form/index.js +4 -3
- package/dist/forms/components/ModalForm/index.d.ts +3 -2
- package/dist/forms/components/ModalForm/index.js +3 -2
- package/dist/forms/hooks/useForm.d.ts +2 -1
- package/dist/forms/hooks/useForm.js +10 -2
- package/dist/forms/hooks/useModalForm.js +8 -1
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ export interface FormProps extends ClassNameProps<FormClassNames> {
|
|
|
8
8
|
id?: string;
|
|
9
9
|
onSubmit?: React.FormEventHandler<HTMLFormElement>;
|
|
10
10
|
ref?: React.Ref<HTMLFormElement>;
|
|
11
|
+
uncaughtErrors: Record<string, string> | null;
|
|
11
12
|
}
|
|
12
|
-
declare function Form({ action, children, classNames, classNameProps, id, onSubmit, ref, }: Readonly<FormProps>): React.ReactElement;
|
|
13
|
+
declare function Form({ action, children, classNames, classNameProps, id, onSubmit, uncaughtErrors, ref, }: Readonly<FormProps>): React.ReactElement;
|
|
13
14
|
export default Form;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useClassNames } from '../../../styles/context';
|
|
4
4
|
import tw from '../../../styles/classnames/utility/tw';
|
|
5
|
-
|
|
5
|
+
import { InfoPanel } from '../../../components';
|
|
6
|
+
function Form({ action, children, classNames, classNameProps, id, onSubmit, uncaughtErrors, ref, }) {
|
|
6
7
|
const componentClassNames = useClassNames('form', { props: classNameProps }, classNames);
|
|
7
|
-
return (
|
|
8
|
+
return (_jsxs("form", { ref: ref, action: action, className: tw(componentClassNames?.root), id: id, onSubmit: onSubmit, children: [uncaughtErrors && Object.keys(uncaughtErrors).length ? (_jsx(InfoPanel, { variant: "error", children: Object.values(uncaughtErrors).map((item, index) => (_jsx("p", { children: item }, index))) })) : null, children] }));
|
|
8
9
|
}
|
|
9
10
|
export default Form;
|
|
@@ -3,11 +3,12 @@ import type { FormProps } from '../Form';
|
|
|
3
3
|
export interface ModalFormProps {
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
formProps: Omit<FormProps, 'children'>;
|
|
6
|
-
hasServerError?: boolean;
|
|
7
6
|
hasSubmit?: boolean;
|
|
8
7
|
isDisabled?: boolean;
|
|
9
8
|
modalProps: Omit<ModalProps, 'children'>;
|
|
9
|
+
serverError?: string | null;
|
|
10
10
|
submitLabel?: string;
|
|
11
|
+
validationErrors?: string[];
|
|
11
12
|
}
|
|
12
|
-
declare function ModalForm({ children, formProps,
|
|
13
|
+
declare function ModalForm({ children, formProps, hasSubmit, isDisabled, modalProps, serverError, submitLabel, validationErrors, }: Readonly<ModalFormProps>): React.ReactElement;
|
|
13
14
|
export default ModalForm;
|
|
@@ -4,12 +4,13 @@ import Modal from '../../../components/modals/Modal';
|
|
|
4
4
|
import ModalActions from '../../../components/modals/ModalActions';
|
|
5
5
|
import useSearchParamsHref from '../../../hooks/useSearchParamsHref';
|
|
6
6
|
import Form from '../Form';
|
|
7
|
-
|
|
7
|
+
import { InfoPanel } from '../../../components';
|
|
8
|
+
function ModalForm({ children, formProps, hasSubmit = true, isDisabled, modalProps, serverError, submitLabel, validationErrors, }) {
|
|
8
9
|
const { setSearchParamsHref } = useSearchParamsHref();
|
|
9
10
|
function handleCancel() {
|
|
10
11
|
setSearchParamsHref('action', null);
|
|
11
12
|
}
|
|
12
|
-
return (_jsx(Modal, { ...modalProps, children: _jsxs(Form, { ...formProps, children: [children,
|
|
13
|
+
return (_jsx(Modal, { ...modalProps, children: _jsxs(Form, { ...formProps, children: [serverError ? _jsx(InfoPanel, { variant: "error", children: serverError }) : null, validationErrors?.length ? (_jsx(InfoPanel, { variant: "error", children: JSON.stringify(validationErrors) })) : null, children, _jsx(ModalActions, { actions: [
|
|
13
14
|
{ label: 'Cancel', onClick: handleCancel },
|
|
14
15
|
...(modalProps.actions || []),
|
|
15
16
|
hasSubmit
|
|
@@ -24,7 +24,7 @@ export interface UseFormArgs<Request, Response> extends UseSuccessArgs<Response>
|
|
|
24
24
|
defaults?: Default<Request>;
|
|
25
25
|
onError?: (message: string) => void;
|
|
26
26
|
onSubmit?: (formData: Request) => FormResponse<Response>;
|
|
27
|
-
onValidation?: (errors: Record<string, string>) => void;
|
|
27
|
+
onValidation?: (errors: Record<string, string>, uncaughtErrors?: Record<string, string>) => void;
|
|
28
28
|
toasts?: ToastsArgs | false;
|
|
29
29
|
}
|
|
30
30
|
export interface UseFormReturn<Request> {
|
|
@@ -38,6 +38,7 @@ export interface UseFormReturn<Request> {
|
|
|
38
38
|
setFormData: <K extends keyof Request>(key: K, value: Request[K]) => void;
|
|
39
39
|
submitForm: () => void;
|
|
40
40
|
}
|
|
41
|
+
export declare const DEFAULT_TOAST_MESSAGES: Record<keyof ToastsArgs, string>;
|
|
41
42
|
/**
|
|
42
43
|
* ## Overview
|
|
43
44
|
*
|
|
@@ -4,7 +4,7 @@ import { formatTitle } from '@sqrzro/utility';
|
|
|
4
4
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
|
5
5
|
import useSuccess from '../../hooks/useSuccess';
|
|
6
6
|
import useToast from '../../hooks/useToast';
|
|
7
|
-
const DEFAULT_TOAST_MESSAGES = {
|
|
7
|
+
export const DEFAULT_TOAST_MESSAGES = {
|
|
8
8
|
server: 'There was a problem submitting your request. Please try again later.',
|
|
9
9
|
success: 'Your request was submitted successfully.',
|
|
10
10
|
validation: 'There was a problem with your submission. Please check the form and try again.',
|
|
@@ -121,9 +121,11 @@ function getToastMessage(key, toasts) {
|
|
|
121
121
|
*/
|
|
122
122
|
function useForm({ defaults = {}, onError, onSubmit, onSuccess, onValidation, redirectOnSuccess, refreshOnSuccess, toasts, }) {
|
|
123
123
|
const ref = useRef(null);
|
|
124
|
+
const usedFields = useRef(new Set());
|
|
124
125
|
const { handleSuccess } = useSuccess({ onSuccess, redirectOnSuccess, refreshOnSuccess });
|
|
125
126
|
const [isLoading] = useTransition();
|
|
126
127
|
const [errors, setErrors] = useState(null);
|
|
128
|
+
const [uncaughtErrors, setUncaughtErrors] = useState(null);
|
|
127
129
|
const [data, setData] = useState(defaults);
|
|
128
130
|
const { toastError, toastSuccess } = useToast();
|
|
129
131
|
function setFormData(key, value) {
|
|
@@ -141,8 +143,12 @@ function useForm({ defaults = {}, onError, onSubmit, onSuccess, onValidation, re
|
|
|
141
143
|
toastError(getToastMessage('server', toasts));
|
|
142
144
|
}
|
|
143
145
|
function handleFormValidation(messages) {
|
|
144
|
-
|
|
146
|
+
const uncaughtMessages = Object.keys(messages)
|
|
147
|
+
.filter((key) => !usedFields.current.has(key))
|
|
148
|
+
.reduce((acc, key) => ({ ...acc, [key]: messages[key] }), {});
|
|
149
|
+
onValidation?.(messages, uncaughtMessages);
|
|
145
150
|
setErrors(messages);
|
|
151
|
+
setUncaughtErrors(uncaughtMessages);
|
|
146
152
|
toastError(getToastMessage('validation', toasts));
|
|
147
153
|
}
|
|
148
154
|
async function handleSubmit() {
|
|
@@ -162,6 +168,7 @@ function useForm({ defaults = {}, onError, onSubmit, onSuccess, onValidation, re
|
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
function fieldProps(name, label) {
|
|
171
|
+
usedFields.current.add(name);
|
|
165
172
|
return {
|
|
166
173
|
error: getErrorsForField(errors, name),
|
|
167
174
|
label: getLabel(name, label),
|
|
@@ -186,6 +193,7 @@ function useForm({ defaults = {}, onError, onSubmit, onSuccess, onValidation, re
|
|
|
186
193
|
formProps: {
|
|
187
194
|
action: handleSubmit,
|
|
188
195
|
ref,
|
|
196
|
+
uncaughtErrors,
|
|
189
197
|
},
|
|
190
198
|
isLoading,
|
|
191
199
|
resetForm,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { useRouter } from 'next/navigation';
|
|
3
4
|
import useSearchParamsHref from '../../hooks/useSearchParamsHref';
|
|
4
|
-
import useForm from './useForm';
|
|
5
|
+
import useForm, { DEFAULT_TOAST_MESSAGES } from './useForm';
|
|
5
6
|
function useModalForm({ actions, icon, submitLabel, title, ...useFormArgs }) {
|
|
7
|
+
const [serverError, setServerError] = useState(null);
|
|
6
8
|
const { setSearchParamsHref } = useSearchParamsHref();
|
|
7
9
|
const router = useRouter();
|
|
8
10
|
function handleSuccess(response) {
|
|
@@ -14,6 +16,10 @@ function useModalForm({ actions, icon, submitLabel, title, ...useFormArgs }) {
|
|
|
14
16
|
}
|
|
15
17
|
const useFormReturn = useForm({
|
|
16
18
|
...useFormArgs,
|
|
19
|
+
onError: (message) => {
|
|
20
|
+
setServerError(DEFAULT_TOAST_MESSAGES.server);
|
|
21
|
+
useFormArgs.onError?.(message);
|
|
22
|
+
},
|
|
17
23
|
onSuccess: handleSuccess,
|
|
18
24
|
toasts: false,
|
|
19
25
|
});
|
|
@@ -22,6 +28,7 @@ function useModalForm({ actions, icon, submitLabel, title, ...useFormArgs }) {
|
|
|
22
28
|
formProps: {
|
|
23
29
|
formProps: useFormReturn.formProps,
|
|
24
30
|
modalProps: { actions, icon, title },
|
|
31
|
+
serverError,
|
|
25
32
|
submitLabel,
|
|
26
33
|
},
|
|
27
34
|
};
|