@simoncomputing/mui-bueno-v3 0.1.16 → 0.1.18
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/README.md +1 -1
- package/dist/components/Form/Inputs/TextField/TextField.d.ts +3 -3
- package/dist/components/Form/MForm/MForm2.d.ts +115 -0
- package/dist/components/Form/MForm/MFormProvider2.d.ts +225 -0
- package/dist/index.cjs.js +100 -100
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +6166 -6059
- package/dist/index.umd.js +100 -100
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ In the application repo:
|
|
|
41
41
|
|
|
42
42
|
```shell
|
|
43
43
|
# Update package to look for this file in the proper directory.
|
|
44
|
-
npm i ~/git/mui-bueno-v3/simoncomputing-mui-bueno-v3-0.1.
|
|
44
|
+
npm i ~/git/mui-bueno-v3/simoncomputing-mui-bueno-v3-0.1.18.tgz
|
|
45
45
|
|
|
46
46
|
# Install
|
|
47
47
|
npm install
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TypographyProps, TextFieldProps as MuiTextFieldProps } from '@mui/material';
|
|
2
|
-
import * as React from
|
|
2
|
+
import * as React from 'react';
|
|
3
3
|
type BaseTextFieldProps = {
|
|
4
4
|
/**
|
|
5
5
|
* Name and ID of the component. Must match a key from initialValues in react-hook-form.
|
|
@@ -46,13 +46,13 @@ type BaseTextFieldProps = {
|
|
|
46
46
|
/**
|
|
47
47
|
* Margin for the FormControl
|
|
48
48
|
*/
|
|
49
|
-
margin?:
|
|
49
|
+
margin?: 'dense' | 'none' | 'normal';
|
|
50
50
|
/**
|
|
51
51
|
* By default, TextField will trim leading and trailing whitespace on blur. To disable this, set `noTrimOnBlur` to true.
|
|
52
52
|
*/
|
|
53
53
|
noTrimOnBlur?: boolean;
|
|
54
54
|
};
|
|
55
|
-
export type TextFieldProps = BaseTextFieldProps & Omit<MuiTextFieldProps,
|
|
55
|
+
export type TextFieldProps = BaseTextFieldProps & Omit<MuiTextFieldProps, 'defaultValue' | 'error' | 'fullWidth' | 'select' | 'SelectProps' | 'value'>;
|
|
56
56
|
/**
|
|
57
57
|
* The `TextField` is a MUI input. For use with validation, view the examples at the bottom of this page.
|
|
58
58
|
*/
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { FieldValues } from 'react-hook-form';
|
|
2
|
+
import { MFormProvider2Props } from './MFormProvider2';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* # MForm2 Documentation
|
|
6
|
+
*
|
|
7
|
+
* ## What this module is
|
|
8
|
+
* `MForm2` is a **thin convenience wrapper** around `MFormProvider2` that:
|
|
9
|
+
* - Renders the actual HTML `<form>` element.
|
|
10
|
+
* - Wires the form’s `onSubmit` event to the provider’s `submit()` function.
|
|
11
|
+
* - Preserves the ability to use either:
|
|
12
|
+
* - **Normal children** (`ReactNode`), or
|
|
13
|
+
* - **Render-prop children** that receive `{ methods, submit }`.
|
|
14
|
+
*
|
|
15
|
+
* `MForm2` exists so most consumers don’t need to manually place `<form>` tags or call `submit()`.
|
|
16
|
+
*
|
|
17
|
+
* ## Responsibilities
|
|
18
|
+
* 1) Delegates all RHF setup/ownership and submit logic to `MFormProvider2`.
|
|
19
|
+
* 2) Adds a `<form>` boundary with:
|
|
20
|
+
* - `noValidate` to disable browser-native validation bubbles
|
|
21
|
+
* - `onSubmit` to call `submit()` (which runs RHF validation + submit wrapper)
|
|
22
|
+
* 3) Exposes basic styling/identification props for the `<form>`:
|
|
23
|
+
* - `formId`, `className`, `style`
|
|
24
|
+
*
|
|
25
|
+
* ## What MForm2 does NOT do
|
|
26
|
+
* - It does not call `useForm()` directly.
|
|
27
|
+
* - It does not implement validation logic itself.
|
|
28
|
+
* - It does not map server errors itself.
|
|
29
|
+
* - It does not manage reinitialization itself.
|
|
30
|
+
* All of those are handled by `MFormProvider2`.
|
|
31
|
+
*
|
|
32
|
+
* ## Props (high level)
|
|
33
|
+
* `MForm2Props<T>` is:
|
|
34
|
+
* - all props accepted by `MFormProvider2` (owned or controlled),
|
|
35
|
+
* - plus `MForm2BaseProps` for the `<form>` element itself.
|
|
36
|
+
*
|
|
37
|
+
* ### Base props (MForm2BaseProps)
|
|
38
|
+
* - `formId?: string`
|
|
39
|
+
* - Applied as the `<form id="...">`.
|
|
40
|
+
* - Useful when your submit button is outside the form (e.g., MUI DialogActions):
|
|
41
|
+
* `<Button type="submit" form="your-form-id">Save</Button>`
|
|
42
|
+
*
|
|
43
|
+
* - `className?: string`
|
|
44
|
+
* - Passed to the `<form>` element.
|
|
45
|
+
*
|
|
46
|
+
* - `style?: React.CSSProperties`
|
|
47
|
+
* - Inline styles for the `<form>` element.
|
|
48
|
+
*
|
|
49
|
+
* ### Provider props (from MFormProvider2Props<T>)
|
|
50
|
+
* MForm2 supports both provider modes via the same prop union:
|
|
51
|
+
*
|
|
52
|
+
* **Controlled mode**
|
|
53
|
+
* - pass `methods: UseFormReturn<T>` to re-use an existing RHF form instance
|
|
54
|
+
*
|
|
55
|
+
* **Owned mode**
|
|
56
|
+
* - pass `defaultValues: DefaultValues<T>` (required)
|
|
57
|
+
* - optionally pass `resolver`, `useFormProps`, `reinitializeKey`, etc.
|
|
58
|
+
*
|
|
59
|
+
* ## Default values (source of truth)
|
|
60
|
+
* MForm2 does not define its own default values. It forwards props to `MFormProvider2`, which chooses:
|
|
61
|
+
*
|
|
62
|
+
* - Controlled mode: default values are whatever the caller used when creating `methods` via `useForm({ defaultValues })`.
|
|
63
|
+
* - Owned mode: default values come from the required `defaultValues` prop.
|
|
64
|
+
*
|
|
65
|
+
* There are no additional default values introduced by MForm2 itself; it only forwards and wraps.
|
|
66
|
+
*
|
|
67
|
+
* ## Render-prop children
|
|
68
|
+
* Children can be either:
|
|
69
|
+
* - Regular JSX:
|
|
70
|
+
* ```tsx
|
|
71
|
+
* <MForm2 ...>
|
|
72
|
+
* <MyFields />
|
|
73
|
+
* </MForm2>
|
|
74
|
+
* ```
|
|
75
|
+
* - Or a render function that receives:
|
|
76
|
+
* - `methods`: RHF methods object (UseFormReturn<T>)
|
|
77
|
+
* - `submit`: programmatic submit function (runs RHF handleSubmit)
|
|
78
|
+
* ```tsx
|
|
79
|
+
* <MForm2 ...>
|
|
80
|
+
* {({ methods, submit }) => (
|
|
81
|
+
* <>
|
|
82
|
+
* <MyFields />
|
|
83
|
+
* <button type="button" onClick={() => void submit()}>Save</button>
|
|
84
|
+
* </>
|
|
85
|
+
* )}
|
|
86
|
+
* </MForm2>
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* ## Submit behavior
|
|
90
|
+
* The `<form onSubmit>` handler:
|
|
91
|
+
* - calls `e.preventDefault()` to avoid browser navigation,
|
|
92
|
+
* - calls `submit()` from `MFormProvider2`, which:
|
|
93
|
+
* - runs RHF validation
|
|
94
|
+
* - calls `onSubmit(values)` when valid
|
|
95
|
+
* - calls `onInvalid(errors)` when invalid (if provided)
|
|
96
|
+
* - optionally maps Axios 422 field errors into RHF errors
|
|
97
|
+
*
|
|
98
|
+
* ## Testing note
|
|
99
|
+
* In unit tests, prefer submitting via user interaction when possible:
|
|
100
|
+
* - `await user.click(button)` or `fireEvent.submit(form)`
|
|
101
|
+
* If you call `submitRef.current()` programmatically, ensure the test awaits resulting UI changes
|
|
102
|
+
* (e.g., `await waitFor(...)` / `await findBy...`) to avoid act warnings.
|
|
103
|
+
*/
|
|
104
|
+
export type MForm2BaseProps = {
|
|
105
|
+
formId?: string;
|
|
106
|
+
className?: string;
|
|
107
|
+
style?: React.CSSProperties;
|
|
108
|
+
};
|
|
109
|
+
export type MForm2Props<T extends FieldValues> = MFormProvider2Props<T> & MForm2BaseProps;
|
|
110
|
+
/**
|
|
111
|
+
* MForm2:
|
|
112
|
+
* - Wraps children with MFormProvider2
|
|
113
|
+
* - Renders a <form> and wires onSubmit to provider's submit()
|
|
114
|
+
*/
|
|
115
|
+
export declare function MForm2<T extends FieldValues>(props: MForm2Props<T>): React.JSX.Element;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { DefaultValues, FieldValues, Resolver, SubmitErrorHandler, SubmitHandler, UseFormProps, UseFormReturn } from 'react-hook-form';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* # MFormProvider2 Documentation
|
|
5
|
+
*
|
|
6
|
+
* ## What this module is
|
|
7
|
+
* `MFormProvider2` is a **thin React Hook Form (RHF) wrapper** that:
|
|
8
|
+
* - Provides a `FormProvider` context for descendants (so `useFormContext()` works).
|
|
9
|
+
* - Standardizes submit behavior (including optional Axios 422 → field error mapping).
|
|
10
|
+
* - Supports two modes:
|
|
11
|
+
* - **Controlled**: you create the RHF methods with `useForm()` elsewhere and pass `methods`.
|
|
12
|
+
* - **Owned**: this provider calls `useForm()` internally using `defaultValues`/`resolver`.
|
|
13
|
+
* - Optionally exposes a programmatic submit function via:
|
|
14
|
+
* - `submitRef` (imperative ref),
|
|
15
|
+
* - and/or an internal context + `useMForm2()` hook.
|
|
16
|
+
*
|
|
17
|
+
* Importantly, this component does **not** render a `<form>` element. That is handled by `MForm2`
|
|
18
|
+
* (or any parent component that wants to place the `<form>` tag wherever it needs to).
|
|
19
|
+
*
|
|
20
|
+
* ## When to use MFormProvider2 vs MForm2
|
|
21
|
+
* - Use **MFormProvider2** when you need RHF context and submit helpers but want to control the `<form>` tag placement.
|
|
22
|
+
* - Use **MForm2** (a separate wrapper) when you want a ready-to-use `<form>` element around your children.
|
|
23
|
+
*
|
|
24
|
+
* ## Primary responsibilities
|
|
25
|
+
* 1) **RHF context**: wraps children with `<FormProvider {...methods}>`.
|
|
26
|
+
* 2) **Submit orchestration**:
|
|
27
|
+
* - Calls `methods.handleSubmit(submitWrapped, onInvalid)`
|
|
28
|
+
* - `submitWrapped` calls your `onSubmit(values)` and catches optional Axios 422 errors.
|
|
29
|
+
* 3) **Server validation errors** (optional):
|
|
30
|
+
* - If `applyAxios422Errors` is enabled and the submit throws an axios-ish 422 error, it maps response field errors
|
|
31
|
+
* to RHF’s `setError()` so the UI can display them like any other validation error.
|
|
32
|
+
* 4) **Programmatic submit**:
|
|
33
|
+
* - Exposes `submit()` to render-prop children,
|
|
34
|
+
* - optionally stores it into `submitRef.current`,
|
|
35
|
+
* - optionally provides it via `useMForm2()` context.
|
|
36
|
+
*
|
|
37
|
+
* ## Concepts: Controlled vs Owned
|
|
38
|
+
*
|
|
39
|
+
* ### Controlled mode
|
|
40
|
+
* You provide the RHF methods object:
|
|
41
|
+
* ```ts
|
|
42
|
+
* const methods = useForm<MyValues>({ defaultValues });
|
|
43
|
+
* <MFormProvider2 methods={methods} onSubmit={...}>{...}</MFormProvider2>
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* Use this when:
|
|
47
|
+
* - You need `methods` in the parent component (e.g., `useFieldArray()` at the dialog level),
|
|
48
|
+
* - You want to configure RHF outside of the wrapper.
|
|
49
|
+
*
|
|
50
|
+
* ### Owned mode
|
|
51
|
+
* You provide `defaultValues` (and optionally `resolver`, `useFormProps`, etc.):
|
|
52
|
+
* ```ts
|
|
53
|
+
* <MFormProvider2 defaultValues={...} resolver={...} onSubmit={...}>{...}</MFormProvider2>
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* Use this when:
|
|
57
|
+
* - You want a very simple call site,
|
|
58
|
+
* - You don’t need to share methods with a parent component.
|
|
59
|
+
*
|
|
60
|
+
* ## Error mapping (Axios 422 → RHF errors)
|
|
61
|
+
*
|
|
62
|
+
* Many APIs return validation errors in a 422 response like:
|
|
63
|
+
* ```json
|
|
64
|
+
* { "Name": "Name is required" }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* If `applyAxios422Errors` is true (default is true), `submitWrapped` catches that error and:
|
|
68
|
+
* - Lowercases the first character of the field name (`Name` → `name`) to match typical TS/RHF casing
|
|
69
|
+
* - Applies the message to RHF via `setError("name", { message: "..." })`
|
|
70
|
+
*
|
|
71
|
+
* ### Custom mapping
|
|
72
|
+
* If your backend response shape differs, provide `mapAxios422` to adapt it.
|
|
73
|
+
*
|
|
74
|
+
* ## Reinitialization (owned mode only)
|
|
75
|
+
* Owned mode supports `reinitializeKey`: when the key changes, the provider calls `methods.reset(defaultValues)`.
|
|
76
|
+
* This is intentionally more predictable than “reset whenever defaultValues changes”.
|
|
77
|
+
*
|
|
78
|
+
* A common pattern is:
|
|
79
|
+
* - `reinitializeKey = divisionId` or a dialog open counter
|
|
80
|
+
* - `defaultValues` comes from your loaded entity
|
|
81
|
+
*
|
|
82
|
+
* ## validateOnMount
|
|
83
|
+
* If `validateOnMount` is true (default is false), the provider calls `methods.trigger()` after mount. This runs validation immediately.
|
|
84
|
+
* (Note: this can cause async updates in tests; prefer `findBy...`/`waitFor` to await the UI.)
|
|
85
|
+
*
|
|
86
|
+
* ## Programmatic submit
|
|
87
|
+
*
|
|
88
|
+
* There are three ways to submit without a submit button:
|
|
89
|
+
*
|
|
90
|
+
* 1) Render-prop children receive `submit()`:
|
|
91
|
+
* ```tsx
|
|
92
|
+
* <MFormProvider2 ...>
|
|
93
|
+
* {({ submit }) => <button onClick={() => void submit()}>Save</button>}
|
|
94
|
+
* </MFormProvider2>
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* 2) Use `submitRef`:
|
|
98
|
+
* ```ts
|
|
99
|
+
* const submitRef = useRef<(() => Promise<void>) | null>(null);
|
|
100
|
+
* <MFormProvider2 submitRef={submitRef} ... />
|
|
101
|
+
* // later
|
|
102
|
+
* await submitRef.current?.();
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* 3) Use `useMForm2()` anywhere under the provider:
|
|
106
|
+
* ```ts
|
|
107
|
+
* const { submit } = useMForm2<MyValues>();
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* ## File-level map of what’s below
|
|
111
|
+
* - Server error helpers (`applyServerErrors`, `Axios422Mapper`)
|
|
112
|
+
* - Optional context + `useMForm2()`
|
|
113
|
+
* - Prop types for owned/controlled
|
|
114
|
+
* - `MFormProvider2WithMethods`: shared implementation given a `methods` object
|
|
115
|
+
* - `MFormProvider2ControlledInner`: controlled mode implementation
|
|
116
|
+
* - `MFormProvider2OwnedInner`: owned mode implementation (calls `useForm()`)
|
|
117
|
+
* - `MFormProvider2`: public selector (chooses controlled vs owned)
|
|
118
|
+
*/
|
|
119
|
+
/** ---------------- Server errors ---------------- */
|
|
120
|
+
export type ServerErrorMap = Record<string, string>;
|
|
121
|
+
/**
|
|
122
|
+
* Apply the server errors to RHF errors.
|
|
123
|
+
* Server errors are a map of fieldName -> error message.
|
|
124
|
+
*
|
|
125
|
+
* Notes:
|
|
126
|
+
* - `focusFirst` (default is true) controls whether RHF should focus the first errored field.
|
|
127
|
+
* - Field names are normalized (`Name` -> `name`) via `lowerCaseFieldName`.
|
|
128
|
+
*/
|
|
129
|
+
export declare function applyServerErrors<T extends FieldValues>(setError: UseFormReturn<T>['setError'], errors: ServerErrorMap, focusFirst?: boolean): void;
|
|
130
|
+
/**
|
|
131
|
+
* Converts a thrown error (usually Axios) into either:
|
|
132
|
+
* - `{ ok: true, errors: ServerErrorMap }` if it is a 422 validation error, or
|
|
133
|
+
* - `{ ok: false }` if it is not.
|
|
134
|
+
*
|
|
135
|
+
* This is intentionally generic so applications can replace it with their own mapping logic.
|
|
136
|
+
*/
|
|
137
|
+
export type Axios422Mapper = (err: unknown) => {
|
|
138
|
+
ok: true;
|
|
139
|
+
errors: ServerErrorMap;
|
|
140
|
+
} | {
|
|
141
|
+
ok: false;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Default mapping for axios-ish errors:
|
|
145
|
+
* - expects `err.response.status === 422`
|
|
146
|
+
* - expects `err.response.data` to be an object of `{ field: message }`
|
|
147
|
+
*/
|
|
148
|
+
export declare const defaultAxios422Mapper: Axios422Mapper;
|
|
149
|
+
/** ---------------- Context (optional) ---------------- */
|
|
150
|
+
/**
|
|
151
|
+
* Context is optional but convenient. It allows any descendant to access:
|
|
152
|
+
* - `submit()` (programmatic submit),
|
|
153
|
+
* - RHF `methods`,
|
|
154
|
+
* - and `applyServerErrors()` without needing prop drilling.
|
|
155
|
+
*/
|
|
156
|
+
type MForm2ContextValue<T extends FieldValues> = {
|
|
157
|
+
submit: () => Promise<void>;
|
|
158
|
+
methods: UseFormReturn<T>;
|
|
159
|
+
applyServerErrors: (errors: ServerErrorMap, focusFirst?: boolean) => void;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Hook to access MFormProvider2 utilities from descendants.
|
|
163
|
+
* Must be called under `<MFormProvider2>` (or `<MForm2>` if it wraps provider+form).
|
|
164
|
+
*/
|
|
165
|
+
export declare function useMForm2<T extends FieldValues>(): MForm2ContextValue<T>;
|
|
166
|
+
/** ---------------- Props ---------------- */
|
|
167
|
+
/**
|
|
168
|
+
* Render-prop signature for children:
|
|
169
|
+
* - `methods`: RHF methods object
|
|
170
|
+
* - `submit`: programmatic submit (runs handleSubmit with error mapping + onInvalid)
|
|
171
|
+
*/
|
|
172
|
+
export type MFormProvider2RenderArgs<T extends FieldValues> = {
|
|
173
|
+
methods: UseFormReturn<T>;
|
|
174
|
+
submit: () => Promise<void>;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Common props shared by owned and controlled usage.
|
|
178
|
+
*
|
|
179
|
+
* - `onSubmit`: called with validated values
|
|
180
|
+
* - `onInvalid`: called with RHF errors when invalid
|
|
181
|
+
* - `validateOnMount`: runs validation immediately on mount via `trigger()`
|
|
182
|
+
* - `applyAxios422Errors` + `mapAxios422`: server validation mapping support
|
|
183
|
+
* - `submitRef`: imperative handle to the `submit()` function
|
|
184
|
+
* - `children`: ReactNode or render-prop (receives methods+submit)
|
|
185
|
+
*/
|
|
186
|
+
type CommonProps<T extends FieldValues> = {
|
|
187
|
+
onSubmit: SubmitHandler<T> | ((values: T) => Promise<void>);
|
|
188
|
+
onInvalid?: SubmitErrorHandler<T>;
|
|
189
|
+
validateOnMount?: boolean;
|
|
190
|
+
applyAxios422Errors?: boolean;
|
|
191
|
+
mapAxios422?: Axios422Mapper;
|
|
192
|
+
submitRef?: React.RefObject<(() => Promise<void>) | null>;
|
|
193
|
+
children: React.ReactNode | ((args: MFormProvider2RenderArgs<T>) => React.ReactNode);
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Controlled mode: consumer provides RHF `methods`.
|
|
197
|
+
*/
|
|
198
|
+
export type MFormProvider2ControlledProps<T extends FieldValues> = CommonProps<T> & {
|
|
199
|
+
methods: UseFormReturn<T>;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Owned mode: provider creates RHF `methods` using `useForm()`.
|
|
203
|
+
*/
|
|
204
|
+
export type MFormProvider2OwnedProps<T extends FieldValues> = CommonProps<T> & {
|
|
205
|
+
defaultValues: DefaultValues<T>;
|
|
206
|
+
resolver?: Resolver<T>;
|
|
207
|
+
shouldFocusError?: boolean;
|
|
208
|
+
useFormProps?: Omit<UseFormProps<T>, 'defaultValues' | 'resolver' | 'shouldFocusError'>;
|
|
209
|
+
/** Reset only when this key changes (owned mode only). */
|
|
210
|
+
reinitializeKey?: string | number;
|
|
211
|
+
resetOptions?: Parameters<UseFormReturn<T>['reset']>[1];
|
|
212
|
+
};
|
|
213
|
+
export type MFormProvider2Props<T extends FieldValues> = MFormProvider2ControlledProps<T> | MFormProvider2OwnedProps<T>;
|
|
214
|
+
/** ---------------- Public provider ---------------- */
|
|
215
|
+
/**
|
|
216
|
+
* Public entry point:
|
|
217
|
+
* - Uses a discriminant check: `'methods' in props`
|
|
218
|
+
* - If present -> controlled mode
|
|
219
|
+
* - Otherwise -> owned mode
|
|
220
|
+
*
|
|
221
|
+
* Note: This is safe with React hooks because hooks are only called inside the
|
|
222
|
+
* owned-mode component, not conditionally within a single component body.
|
|
223
|
+
*/
|
|
224
|
+
export declare function MFormProvider2<T extends FieldValues>(props: MFormProvider2Props<T>): React.JSX.Element;
|
|
225
|
+
export {};
|