@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.
@@ -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
- function Form({ action, children, classNames, classNameProps, id, onSubmit, ref, }) {
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 (_jsx("form", { ref: ref, action: action, className: tw(componentClassNames?.root), id: id, onSubmit: onSubmit, children: children }));
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, hasServerError, hasSubmit, isDisabled, modalProps, submitLabel, }: Readonly<ModalFormProps>): React.ReactElement;
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
- function ModalForm({ children, formProps, hasServerError, hasSubmit = true, isDisabled, modalProps, submitLabel, }) {
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, hasServerError ? _jsx("div", { children: "SERVER ERROR" }) : null, _jsx(ModalActions, { actions: [
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
- onValidation?.(messages);
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
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sqrzro/ui",
3
3
  "type": "module",
4
- "version": "4.0.0-alpha.54",
4
+ "version": "4.0.0-alpha.55",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "ISC",