Releases: edmundhung/conform
v0.4.0-pre.1
What's Changed
- feat!: finialize server validation mode by @edmundhung in #42
Full Changelog: v0.4.0-pre.0...v0.4.0-pre.1
v0.4.0-pre.0
What's Changed
- Add comment about button values by @brandonpittman in #36
- fix(conform-react): remove unwanted state sync in useFieldList by @edmundhung in #38
- feat(conform-dom,conform-react,conform-yup,conform-zod)!: make internal state uncontrolled by @edmundhung in #39
- feat(conform-dom,conform-react,conform-zod,conform-yup)!: server validation by @edmundhung in #40
Full Changelog: v0.3.1...v0.4.0-pre.0
v0.3.1
What's Changed
Conform guide (https://conform.guide)
This includes updated examples and a new Get started guide covering the core ideas behind conform.
I hope you will find it useful 😅
Autofocus first error field
Conform now automatically focus on first error field whenever user tries to initiate a form submission.
This applies to all native input fields with no changes required.
However, if you are working with controlled inputs and you want to have the invalid fields focused as well, you will need to pass the ref object provided by useControlledInput()
to the input.
For example, you need to pass the ref object as inputRef
with material-ui
:
export default function ArticleForm() {
const formProps = useForm();
const { category } = useFieldset<Article>(formProps.ref);
const [categoryInput, control] = useControlledInput(category.config);
return (
<form {...formProps}>
<Stack spacing={3}>
<input {...categoryInput} required />
<TextField
label="Category"
inputRef={control.ref}
value={control.value}
onChange={control.onChange}
onBlur={control.onBlur}
error={Boolean(category.error)}
helperText={category.error}
inputProps={{
// To disable browser report caused by the required
// attribute set by mui input
onInvalid: control.onInvalid,
}}
select
required
>
<MenuItem value="">Please select</MenuItem>
<MenuItem value="a">Option A</MenuItem>
<MenuItem value="b">Option B</MenuItem>
<MenuItem value="c">Option C</MenuItem>
</TextField>
<Button type="submit" variant="contained">
Submit
</Button>
</Stack>
</form>
);
}
You can check out the full example here
New Contributors
- @abenhamdine made their first contribution in #26
- @brandonpittman made their first contribution in #28
Thank you!
Full Changelog: v0.3.0...v0.3.1
v0.3.0
What's Changed
New packages
Breaking Changes
useControlledInput
now returns the props to be applied on the input element instead of giving you an element directly (#19)
import { useFieldset, useControlledInput } from '@conform-to/react';
import { Select, MenuItem } from '@mui/material';
function MuiForm() {
const [fieldsetProps, { category }] = useFieldset(schema);
const [inputProps, control] = useControlledInput(category);
return (
<fieldset {...fieldsetProps}>
{/* Render a shadow input somewhere */}
<input {...inputProps} />
{/* MUI Select is a controlled component */}
<Select
label="Category"
value={control.value}
onChange={control.onChange}
onBlur={control.onBlur}
inputProps={{
onInvalid: control.onInvalid
}}
>
<MenuItem value="">Please select</MenuItem>
<MenuItem value="a">Category A</MenuItem>
<MenuItem value="b">Category B</MenuItem>
<MenuItem value="c">Category C</MenuItem>
</TextField>
</fieldset>
)
}
useFieldList
now expects a form / fieldset ref and support form reset event properly (#20)useForm
no longer accept theonReset
property. Just use the formonReset
event listener if needed (#21)useFieldset
no longer returns theFieldsetProps
, instead it expect a ref object of the form or fieldset element (#21)- The field information returned from
useFieldset
now groups all of the data asconfig
excepterror
as it is a field state (#21) - The
parse
function exported from@conform-to/zod
is now merged withresolve
(#22) - Introduced a new helper
ifNonEmptyString
for zod schema preprocess configuration (#24)
import { z } from 'zod';
import { resolve, ifNonEmptyString } from '@conform-to/zod';
const schema = resolve(
z.object({
// No preprocess is needed for string as empty string
// is already converted to undefined by the resolver
text: z.string({ required_error: 'This field is required' }),
// Cast to number manually
number: z.preprocess(
ifNonEmptyString(Number),
z.number({ required_error: 'This field is required' }),
),
// This is how you will do it without the helper
date: z.preprocess(
(value) => (typeof value === 'string' ? new Date(value) : value),
z.date({ required_error: 'This field is required' }),
),
}),
);
Improvements
- refactor: simplify zod resolver setup in #16
- chore: demonstrate features with additional examples in #23
- chore: docs update in #25
Full Changelog: v0.2.0...v0.3.0
v0.2.0
What's changed
Breaking changes
- Removed the auto type casting preprocess setup on
@conform-to/zod
to enable full control over the form data parsing logic. (#5)
// Before:
const schema = z.object({
number: z.number(), // '1' was casted to 1 automatically
boolean: z.boolean(), // Only checkbox with 'on' value was supported
});
// To achieve the same behaviour now:
const schema = z.object({
number: z.preprocess(value => typeof value !== 'undefined' ? Number(value) : undefined, z.number())
boolean: z.preprocess(value => value === 'on', z.boolean())
});
- The result of
parse
is redesigned (#13)
// New type to represent current form state
interface FormState<T> {
value: FieldsetData<T, string>;
error: FieldsetData<T, string>;
}
// New name of `FormResult`
interface Submission<T> {
// `processed` is renamed to `modified`
state: 'accepted' | 'rejected' | 'modified';
// Only available if state = accepted
data: T;
// This will be always available regardless of state
form: FormState<T>;
}
// Example usage
let action = async ({ request }) => {
const formData = await request.formData();
const submission = parse(formData, schema);
if (submission.state !== 'accepted') {
return json(submission.form);
}
// ... do something else
};
export default function RandomForm() {
const formState = useActionData<FormState<Schema>>();
const formProps = useFieldset(schema, {
defaultValue: formState.value,
error: formState.error,
});
// ....
}
- Renamed
initialValue
todefaultValue
. This affects several places including the options on theuseFieldset
hook (#11)
const [fieldsetProps, { a, b, c }] = useFieldset(schema, {
// Before:
// initialValue: ...
// Now:
defaultValue: ...
})
- The
FieldConfig
type is renamed toFieldProps
with a flattened structure (#12)
// Before
export interface FieldType<T> {
name: string;
initialValue?: FieldsetData<T, string>;
error?: FieldsetData<T, string>;
form?: string;
constraint?: Constraint;
}
// Now
export interface FieldProps<T> extends Constraint {
name: string;
defaultValue?: FieldsetData<T, string>;
error?: FieldsetData<T, string>;
form?: string;
}
New features
- Added new list control commands (#14)
const [fieldList, control] = useFieldList(fieldProps);
// To append a new row (New: with optional defaultValue)
<button {...control.append(defaultValue)}>Append</button>;
// To prepend a new row (New: with optional defaultValue)
<button {...control.prepend(defaultValue)}>Prepend</button>;
// New: To replace a row with another defaultValue
<button {...control.replace(index, defaultValue)}>Replace</button>;
// New: To reorder a particular row to an another index
<button {...control.reorder(fromIndex, toIndex)}>Reorder</button>;
Improvements
- Fixed a case with error report happens before the field is re-validated (#6)
- Error messages are now cleared properly when the form reset (#7)
- Fixed a issue with
conform.input()
incorrectly ignored themultiple
property (#8) - Improved the constraint inferred from zod schema (#9)
- The APIs of the react adapter and zod schema resolver is now documented (#10)
Full Changelog: v0.1.1...v0.2.0
v0.1.1
What's Changed
- feat: add boolean checkbox support by @edmundhung in #4
Full Changelog: v0.1.0...v0.1.1
v0.1.0
release: bump packages version