diff --git a/CHANGELOG.md b/CHANGELOG.md index fc7853a..7025050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +# 2.2.3 (02/08/2021) + +- Fix bug where `form.defaultValues` and `form.values` got overwritten when using utility functions in `useArrayListener`. + +# 2.2.2 (02/08/2021) + +- Fix crash when comparing Date with null Date field. + +# 2.2.1 (25/06/2021) + +- Fix crash on undefined/null array when using useArrayField. + +# 2.2.0 (9/05/2021) + +- `innerRef` prop on `Field` and `FieldError` +- Removed `FormState.setDefaultValues` -> merged with `FormState.setValues` + +# 2.1.0 (29/04/2021) + +- Support `errorClassName`, `dirtyClassName`, `dirtyStyle`, `errorStyle` on Field component + +# 2.0.0 (29/04/2021) + +- Rewrite FormInput -> Field component +- Rewrite FormError -> FieldError +- Removed FormInput, FormSelect, FormTextarea, FormError +- Better naming: renamed useArrayForm -> useArrayField, useChildForm -> useObjectField + # 1.3.2 (26/04/2021) - Update documentation links. diff --git a/README.md b/README.md index c6060e7..6bf673c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,24 @@ -# Typed React form [![NPM](https://img.shields.io/npm/v/typed-react-form.svg)](https://www.npmjs.com/package/typed-react-form) [![NPM Size](https://img.shields.io/bundlephobia/minzip/typed-react-form)](https://bundlephobia.com/result?p=typed-react-form) +

+ Typed React form +

-![vscode typescript](https://github.com/CodeStix/typed-react-form/raw/master/testing/public/thumb.png) +

+ +

-A fast, completely type-checked React form builder, focussed on typescript integration and minimal rerenders. Featuring: +

+ NPM + NPM Size +

-- [Object fields](https://codestix.github.io/typed-react-form/advanced/Object-fields) -- [Array fields](https://codestix.github.io/typed-react-form/advanced/Array-fields) -- [Validation](https://codestix.github.io/typed-react-form/validation) -- [Easily toggle fields](https://codestix.github.io/typed-react-form/advanced/Toggling-a-field) -- [Listeners (subscription based state updates)](https://codestix.github.io/typed-react-form/reference/useListener) +

+ A completely type-checked form builder for React with Typescript +

-**All of this while keeping type-checking!** +- ✔️ **Type-checked**: Make less errors, even field names are strongly typed. +- 🤔 **Simple**: A well [documented](https://codestix.github.io/typed-react-form/), intuitive and easy to understand api. +- :fire: **Fast**: Only rerenders the fields that change if used correctly. This allows you to create huge forms. +- 📦 **Pretty Small**: [![NPM Size](https://img.shields.io/bundlephobia/minzip/typed-react-form)](https://bundlephobia.com/result?p=typed-react-form) ## Install @@ -20,6 +28,17 @@ npm install typed-react-form ## [Documentation here](https://codestix.github.io/typed-react-form/) +## Typescript demos + +### Type-checked field names +![type-checked field names](https://github.com/CodeStix/typed-react-form/raw/master/docs/images/demo-example.gif) + +### Type-checked custom inputs +![type-checked custom inputs](https://github.com/CodeStix/typed-react-form/raw/master/docs/images/demo-custom.gif) + +### Type-checked object/array fields +![type-checked object/array fields](https://github.com/CodeStix/typed-react-form/raw/master/docs/images/demo-objectfield.gif) + ## Javascript/typescript React This library is built from the ground up for React with typescript, but it also works with with vanilla React, without enforced type checking. @@ -28,10 +47,11 @@ This library is built from the ground up for React with typescript, but it also Contributions are welcome. -1. Clone this repo +1. Clone this repo. 2. Install deps using `yarn`. Yarn is required because of the resolutions field in package.json, npm does not support this. -3. Open a new terminal and navigate to `example/`, run `yarn` and `yarn start` to start the testing application. -4. Done! When you edit source code, it will be rebuilt and update the testing application. +3. Run `yarn start`, this will watch source files in `src/` and rebuild on change. +4. Open a new terminal and navigate to `testing/`, run `yarn` and `yarn start` to start the testing application. +5. Done! When you edit source code, it will be rebuilt and update the testing application. ## License diff --git a/Todo.md b/Todo.md index 7407dff..a0e88f1 100644 --- a/Todo.md +++ b/Todo.md @@ -2,7 +2,6 @@ - Nested validators in child forms to improve validation performance - Combine array helpers into one object? This is usefull to pass to other components - Require index for array fields -- Add React.forwardRef to input elements -- Rename `setDefaultValues` to `setAllValues` -- Let `comparePrimitiveObject` compare deep objects too - Field on blur +- Use React.forwardRef instead of innerRef on Field/FieldError +- Rename `FormField` -> `FieldInfo` & better documentation on passed Field props diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md index c7fb167..9619802 100644 --- a/docs/Troubleshooting.md +++ b/docs/Troubleshooting.md @@ -36,13 +36,19 @@ When you use [styled-components](https://github.com/styled-components/styled-com ```tsx // Example styled CustomInput const StyledCustomInput: typeof CustomInput = styled(CustomInput)` - &.typed-form-dirty { + &.field-dirty { background-color: #0001; } - &.typed-form-error { + &.field-error { color: red; font-weight: bold; } `; ``` + +--- + +## Element type is invalid: expected a string (for built-in components) or a class/function but got: undefined + +This sometimes happens after installing this package. Restart your project to fix it. (restart `react-scripts`) diff --git a/docs/_config.yml b/docs/_config.yml index 6f4d6d7..4d32bd7 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,10 +1,12 @@ -theme: "just-the-docs" -baseurl: "/typed-react-form" +# theme: "just-the-docs" +# baseurl: "/typed-react-form" remote_theme: pmarsceill/just-the-docs aux_links: "GitHub": - "https://github.com/CodeStix/typed-react-form" + "NPM": + - "https://www.npmjs.com/package/typed-react-form" aux_links_new_tab: true diff --git a/docs/examples/Auto-disable-submit-button.md b/docs/examples/Auto-disable-submit-button.md index a8f374a..9bac8be 100644 --- a/docs/examples/Auto-disable-submit-button.md +++ b/docs/examples/Auto-disable-submit-button.md @@ -24,7 +24,7 @@ function FormExample() { onSubmit={(ev) => { ev.preventDefault(); console.log("save", form.values); - form.setDefaultValues(form.values); + form.setValues(form.values); }} > diff --git a/docs/images/demo-custom.gif b/docs/images/demo-custom.gif new file mode 100644 index 0000000..d85c67a Binary files /dev/null and b/docs/images/demo-custom.gif differ diff --git a/docs/images/demo-example.gif b/docs/images/demo-example.gif new file mode 100644 index 0000000..e64fcb2 Binary files /dev/null and b/docs/images/demo-example.gif differ diff --git a/docs/images/demo-objectfield.gif b/docs/images/demo-objectfield.gif new file mode 100644 index 0000000..4060608 Binary files /dev/null and b/docs/images/demo-objectfield.gif differ diff --git a/docs/images/thumb.pdn b/docs/images/thumb.pdn new file mode 100755 index 0000000..2130789 Binary files /dev/null and b/docs/images/thumb.pdn differ diff --git a/docs/images/thumb.png b/docs/images/thumb.png new file mode 100755 index 0000000..336b4eb Binary files /dev/null and b/docs/images/thumb.png differ diff --git a/docs/images/thumbextrasmall.png b/docs/images/thumbextrasmall.png new file mode 100755 index 0000000..503eec6 Binary files /dev/null and b/docs/images/thumbextrasmall.png differ diff --git a/docs/images/thumbsmall.png b/docs/images/thumbsmall.png new file mode 100755 index 0000000..49ab650 Binary files /dev/null and b/docs/images/thumbsmall.png differ diff --git a/docs/index.md b/docs/index.md index 8f41a6c..3801cf4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -19,6 +19,8 @@ yarn add typed-react-form This library works with both **Javascript** and **Typescript**. Typescript is certainly preferred because of the enforced type-checking! +**Make sure to restart your project after installing. (restart `react-scripts`)** + ## Step 2: Creating a form ### Using the `useForm` hook @@ -36,6 +38,13 @@ function MyForm() { } ``` + + ### Creating the submit handler Use `form.handleSubmit` to validate before calling your function. It does not get called if there is a validation error, and prevents the page from reloading. @@ -49,14 +58,15 @@ function MyForm() { const form = useForm({ email: "" }); function submit() { - // The form.handleSubmit validates the form before calling this function + // You don't have to use form.handleSubmit, it just validates + // your form before calling this function console.log("submit", form.values); } - // Use the standard html form element, which exposes the onSubmit event. + // Use the standard html form element, which has the onSubmit event. + // Make sure to add type="submit" to your submit button return (
- {/* Make sure to add type="submit" */}
); @@ -78,10 +88,8 @@ function MyForm() { async function submit() { // Implement your submit logic here... console.log("submitting", form.values); - // Fake fetch, by waiting for 500ms - await new Promise((res) => setTimeout(res, 500)); // Optional: set new default values - form.setDefaultValues(form.values); + form.setValues(form.values); } return ( diff --git a/docs/reference/Field.md b/docs/reference/Field.md index b5a949e..96d5ef6 100644 --- a/docs/reference/Field.md +++ b/docs/reference/Field.md @@ -21,7 +21,7 @@ The input transforms its value based on the `type` prop, which **currently suppo It is allowed to use multiple inputs on the same field, all of them will be synchronized. -These inputs are given a `className` when errored (`typed-form-error`) or modified (`typed-form-dirty`) by default. +These inputs are given a `className` when errored (`field-error`) or modified (`field-dirty`) by default. ## Examples @@ -127,7 +127,46 @@ const form = useForm({ ### Styling/custom component -TODO +#### ❌ Inline styling + +You can pass the `style` and `className` props to the `Field` component, but this will get repetitive and annoying fast. + +```tsx + + + + + + + + + +``` + +You should only use the `errorStyle`, `dirtyStyle`, `errorClassName` and `dirtyClassName` props in rare situations where you need specific style overrides for a specific field. + +#### ✔️ Using custom component + +You should pass a custom component to the `as` prop of `Field`. The props required by your component will be placed on the `Field` component (will be type-checked). + + + +Some props of your custom component will be filled in automatically by the `Field` component: + +|`onChange`|The change handler, this can be a `React.ChangeEventHandler` or a `(value: T) => void`, ready to be passed to your underlying input. +|`value`|The current value in string format, ready to be passed to the underlying input element. +|`checked`|The current checked state as boolean, ready to be passed to the underlying input element. +|`disabled`|A boolean that will be true when submitting. +|`field`|A [`FormField`](/typed-react-form/reference/useListener.html#return-value) instance, which contains information about the field like the value, its error, the form it is part of and whether is has been modified. +|`style`|Will merge your passed `style` with `errorStyle` when there is an error on this field and `dirtyStyle` when the field has been modified. +|`className`|Will merge your passed className with `field-error` when there is an error on this field and `field-dirty` when the field has been modified. (These classNames can be changed using the `errorClassName` and `dirtyClassName` props) + +**If you don't like this way of passing props**, you can also [create custom inputs](/typed-react-form/examples/Custom-input) using the useListener hook (advanced). ### Submit @@ -154,13 +193,17 @@ The form or child form that contains the field to bind to this input. The name of the field in the form that will be bound to this input. +#### `as` (`"input"` by default) + +The element/componment to render, this can be a string specifying "input", "select", "textarea" or a custom component. Is "input" by default. The props of the passed custom component are available on this Field component. + #### `errorClassName` and `errorStyle` -The className and/or style to set when there is an error on this field. Default className is `typed-form-error`. +The className and/or style to set when there is an error on this field. Default className is `field-error`. #### `dirtyClassName` and `dirtyStyle` -The className and/or style to set when this field has been modified. Default className is `typed-form-dirty`. +The className and/or style to set when this field has been modified. Default className is `field-dirty`. #### `dateAsNumber` (only type="date") @@ -182,6 +225,9 @@ Make sure you pass along a `value` too, this is the value that will be set when - The value of the checkbox when using primitive arrays. - The on-checked value of the checkbox when using the `setNullOnUncheck/setUndefinedOnUncheck` prop. +#### `innerRef` +The ref prop to pass to the underlaying component/input. + --- ## Custom fields/inputs diff --git a/docs/reference/FormState.md b/docs/reference/FormState.md index 4849483..dd8b101 100644 --- a/docs/reference/FormState.md +++ b/docs/reference/FormState.md @@ -19,7 +19,7 @@ The values of the form. Can be set with `setValues()`. #### `defaultValues` **(readonly)** -The default values of the form. Input elements do not change this. This gets used to reset the form and to calculate dirty flags. Can be set with `setDefaultValues()`. +The default values of the form. Input elements do not change this. This gets used to reset the form and to calculate dirty flags. Can be set with `setValues(?,?,true)`. #### `childMap` **(readonly)** @@ -78,14 +78,7 @@ Sets values _OR_ default values for a form. - `values` **(required)**: The new values/default values to set on this form. - `validate` **(optional)**: Validate? When defined, overrides `validateOnChange`. -- `isDefault` **(optional, false)**: When true, updates the default values instead of the normal values. Only updates one or the other, to set both at the same time, use `setDefaultValues()` or 2 function calls. - -#### `setDefaultValues(values, validate = true, notifyChild = true, notifyParent = true)` - -Set both values _AND_ default values for a form. If you only want to set default values, use `setValues(...,...,true)`. - -- `values` **(required)**: The new values to set on this form. -- `validate` **(optional)**: Validate? When defined, overrides `validateOnChange`. +- `isDefault` **(optional)**: Leave undefined to set both `values` and `defaultValues`. Set to true to only set `defaultValues` and false to only set `values`. #### `validate()` diff --git a/docs/reference/useForm.md b/docs/reference/useForm.md index 59e6aea..55216f1 100644 --- a/docs/reference/useForm.md +++ b/docs/reference/useForm.md @@ -11,7 +11,7 @@ Creates a new form state manager. This hook does not cause a rerender. This hook must be called, unconditionally, at the start of your component, just like the normal React hooks. -**Note for using `setState` along with `useForm`**: This library is built upon the fact that only the things that change should rerender. When using `setState` with the form, a state change causes the whole form to rerender. You can reduce this problem by creating components from the things that use the state, or you can use [custom form state](/typed-react-form/reference/useForm#defaultstate-optional-issubmitting-false). +**Note for using `setState` along with `useForm`**: This library is built upon the fact that only the things that change should rerender. When using `setState` with the form, a state change causes the whole form to rerender. You can reduce this problem by creating components from the things that use the state, or you can use [custom form state](/typed-react-form/reference/useForm.html#defaultstate-optional). `useForm(defaultValues, validator?, validateOnChange = false, validateOnMount = false, defaultState = {isSubmitting: false})` diff --git a/docs/reference/useListener.md b/docs/reference/useListener.md index ccb4bac..6e9af0d 100644 --- a/docs/reference/useListener.md +++ b/docs/reference/useListener.md @@ -88,7 +88,7 @@ function BreadForm() { ## Return value -Returns a object containing the following fields, which you can destruct: +Returns a `FormField` instance containing the following fields, which you can destruct: ```tsx return { @@ -98,8 +98,8 @@ return { dirty, // True if the field is modified (if not default value anymore) error, // The error on this field state, // The state of the form (contains isSubmitting) - form; // The form this field belongs to -} + form // The form this field belongs to (FormState instance) +}; // Example usage const { value, setValue, error } = useListener(form, "fieldname"); diff --git a/package.json b/package.json index 83e91ac..3061f3d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typed-react-form", - "version": "1.3.2", - "description": "A React form form builder, focussed on typescript integration and minimal rerenders.", + "version": "2.2.3", + "description": "A completely type-checked React form builder.", "author": "codestix", "license": "MIT", "repository": "CodeStix/typed-react-form", diff --git a/src/Field.tsx b/src/Field.tsx index a7f19ca..ef7b9ee 100644 --- a/src/Field.tsx +++ b/src/Field.tsx @@ -14,7 +14,7 @@ export type FieldProps = { */ form: FormState; /** - * The name of the field + * The name of the field in form */ name: K; /** @@ -43,14 +43,54 @@ export type FieldProps = { * You can change the behaviour of the default deserializer using the `type` prop. */ deserializer?: Deserializer; + /** + * The class to set when there is an error on this field. + */ + errorClassName?: string; + /** + * The class to set when this field has been modified. + */ + dirtyClassName?: string; + /** + * The style to set when where is an error on this field. + */ + errorStyle?: React.CSSProperties; + /** + * The style to set when this field has been modified. + */ + dirtyStyle?: React.CSSProperties; + /** + * The ref to pass to your input component. + */ + innerRef?: React.Ref; }; +// Note on innerRef: React.forwardRef breaks type-checking + export function Field | keyof JSX.IntrinsicElements = "input">( props: FieldProps & - Omit, "value" | "checked" | "onChange" | "field" | keyof FieldProps | keyof SerializeProps> & + Omit, "value" | "checked" | "onChange" | "field" | "ref" | keyof FieldProps | keyof SerializeProps> & SerializeProps ) { - const { form, as = "input", serializer, dateAsNumber, setNullOnUncheck, setUndefinedOnUncheck, deserializer, hideWhenNull, ...rest } = props; + const { + form, + as = "input", + serializer, + dateAsNumber, + setNullOnUncheck, + setUndefinedOnUncheck, + deserializer, + hideWhenNull, + disabled, + className, + style, + errorClassName, + errorStyle, + dirtyClassName, + dirtyStyle, + innerRef, + ...rest + } = props; const serializeProps = { dateAsNumber, setNullOnUncheck, @@ -63,12 +103,20 @@ export function Field { - let v = "target" in ev ? (["checkbox", "radio"].includes(props.type!) ? ev.target.checked : ev.target.value) : ev; + let v = + typeof ev === "object" && "target" in ev + ? props.type === "checkbox" || props.type === "radio" + ? ev.target.checked + : ev.target.value + : ev; if (typeof v === "string" || typeof v === "boolean") field.setValue((deserializer ?? defaultDeserializer)(v, field.value, serializeProps)); else field.setValue(v); @@ -110,6 +158,9 @@ export function defaultSerializer(currentValue: T, props: SerializeProps): case "datetime-local": case "date": { let dateValue = currentValue as any; + if (dateValue === null || dateValue === undefined || dateValue === "") { + return ""; + } if (typeof dateValue === "string") { let ni = parseInt(dateValue); if (!isNaN(ni)) dateValue = ni; diff --git a/src/FieldError.tsx b/src/FieldError.tsx index 9487f0b..85c2ebb 100644 --- a/src/FieldError.tsx +++ b/src/FieldError.tsx @@ -30,10 +30,14 @@ export function FieldError< */ as?: C; transformer?: (error: ErrorType) => React.ReactNode; - } & Omit, "transformer" | "as" | "name" | "form" | "children" | "field"> + /** + * The ref to pass to your error component. + */ + innerRef?: React.Ref; + } & Omit, "transformer" | "as" | "name" | "form" | "children" | "field" | "ref"> ) { - const { form, as = React.Fragment, transformer, ...rest } = props; + const { form, as = React.Fragment, transformer, innerRef, ...rest } = props; const field = useListener(form, props.name); if (!field.error || typeof field.error === "object") return null; - return React.createElement(as, { ...rest, field, children: transformer ? transformer(field.error) : String(field.error) }); + return React.createElement(as, { ...rest, ref: innerRef, field, children: transformer ? transformer(field.error) : String(field.error) }); } diff --git a/src/form.ts b/src/form.ts index fa12880..b3f2859 100644 --- a/src/form.ts +++ b/src/form.ts @@ -30,7 +30,7 @@ function memberCopy(value: T): T { } else if (typeof value === "object") { return { ...value }; } else { - throw new Error("Can only memberCopy() arrays and objects."); + throw new Error(`Can only memberCopy() arrays and objects, got '${String(value)}'. Probably due to invalid useForm() value.`); } } @@ -62,12 +62,12 @@ export class FormState | undefined, validate?: boolean, - isDefault: boolean = false, + isDefault?: boolean, notifyChild: boolean = true, notifyParent: boolean = true ) { + if (isDefault === undefined) { + this.setValues(values, false, true, notifyChild, notifyParent); + isDefault = false; + } + let keys = Object.keys(isDefault ? this.defaultValues : this.values); let v: typeof values = values ?? {}; addDistinct(keys, Object.keys(v)); @@ -287,18 +295,6 @@ export class FormState, validate: boolean = true, notifyChild: boolean = true, notifyParent: boolean = true) { - this.setValues(values, false, true, notifyChild, notifyParent); - this.setValues(values, validate, false, notifyChild, notifyParent); - } - /** * Force validation on this form. Required when `validateOnChange` is disabled. **This function works with both asynchronous and synchronous validators.** * @returns true if the form is valid. diff --git a/src/hooks.ts b/src/hooks.ts index f8d67f6..43fd27b 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -157,7 +157,7 @@ export function useArrayField< useEffect(() => { let id = parentForm.listen(name, () => { let val = parentForm.values[name] as any; - if (val.length !== oldLength.current) { + if (Array.isArray(val) && val.length !== oldLength.current) { setRender((i) => i + 1); oldLength.current = val.length; } @@ -166,17 +166,17 @@ export function useArrayField< }, []); const append = useCallback((value: NonNullable[any]) => { - form.setValues([...(form.values as any), value] as any); + form.setValues([...(form.values as any), value] as any, true, false); }, []); const remove = useCallback((index: number) => { let newValues = [...(form.values as any)]; newValues.splice(index, 1); - form.setValues(newValues as any); + form.setValues(newValues as any, true, false); }, []); const clear = useCallback(() => { - form.setValues([] as any); + form.setValues([] as any, true, false); }, []); const move = useCallback((from: number, to: number) => { @@ -188,7 +188,7 @@ export function useArrayField< newArr[k] = newArr[k + increment]; } newArr[to] = target; - form.setValues(newArr as any); + form.setValues(newArr as any, true, false); }, []); const swap = useCallback((index: number, newIndex: number) => { @@ -197,7 +197,7 @@ export function useArrayField< } let values = [...(form.values as any)]; [values[index], values[newIndex]] = [values[newIndex], values[index]]; - form.setValues(values as any); + form.setValues(values as any, true, false); }, []); return { diff --git a/testing/src/Fieldform.tsx b/testing/src/Fieldform.tsx index f47bddd..6df4ca2 100644 --- a/testing/src/Fieldform.tsx +++ b/testing/src/Fieldform.tsx @@ -1,51 +1,46 @@ -import React from "react"; -import { useForm, Field, AnyListener, FieldError, FormField, Listener } from "typed-react-form"; +import React, { useRef } from "react"; +import { useForm, Field, AnyListener, ObjectField } from "typed-react-form"; -function Input(props: { value?: string; onChange?: (value: string) => void; style: React.CSSProperties }) { - return props.onChange?.(ev.target.value)} />; -} - -function validate(_: any) { - return { - email: "yikes" - }; -} +const inputStyle: React.CSSProperties = { + color: "gray", + padding: "0.3em" +}; -function Error(props: { children: React.ReactNode; field: FormField }) { - return ( -

- - {props.children} ({props.field.dirty + ""}) - -

- ); +// value and onChange are handled by the Field component +function CustomInput(props: { value: string; onChange: (val: string) => void; myCustomProp?: boolean }) { + return props.onChange(ev.target.value)} />; } export function FieldForm() { - const form = useForm({ email: "", firstName: "", gender: "male" as "male" | "female", enableEmail: true }, validate); + const form = useForm({ firstName: "", lastName: "" }); + const inputRef = useRef(null); function submit() { - console.log("this is epic"); + console.log(form.values); } return (
- - - - - - - - - -

- value: -

-
-                 JSON.stringify(form.values, null, 2)} />
-            
- + + + + +
{String(dirty)}
} /> + ); } diff --git a/testing/src/OneOfObjectArrayForm.tsx b/testing/src/OneOfObjectArrayForm.tsx index 0783d1e..7530d6b 100644 --- a/testing/src/OneOfObjectArrayForm.tsx +++ b/testing/src/OneOfObjectArrayForm.tsx @@ -36,7 +36,7 @@ export default function OneOfObjectArrayForm() { await new Promise((res) => setTimeout(res, 500)); form.setState({ isSubmitting: false }); console.log(form.values); - form.setDefaultValues(form.values); + form.setValues(form.values); }} > View source code diff --git a/testing/src/OneOfObjectForm.tsx b/testing/src/OneOfObjectForm.tsx index 2b9aefa..d1aec2d 100644 --- a/testing/src/OneOfObjectForm.tsx +++ b/testing/src/OneOfObjectForm.tsx @@ -31,7 +31,7 @@ export default function OneOfObjectArrayForm() { await new Promise((res) => setTimeout(res, 500)); form.setState({ isSubmitting: false }); console.log(form.values); - form.setDefaultValues(form.values); + form.setValues(form.values); }} > View source code diff --git a/testing/src/index.css b/testing/src/index.css index 089442a..1d72d84 100644 --- a/testing/src/index.css +++ b/testing/src/index.css @@ -1,7 +1,8 @@ body { margin: 0; padding: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } @@ -32,12 +33,12 @@ small { font-weight: normal; } -.typed-form-dirty { +.field-dirty { outline: 3px solid #0004; transition: 50ms linear; } -.typed-form-error { +.field-error { outline: 3px solid #f009; transition: 50ms linear; }