Giter Club home page Giter Club logo

react-ts-form's People

Contributors

antcar112 avatar chukwumaokere avatar florian-lefebvre avatar ingadi avatar irekrog avatar iway1 avatar kroucher avatar mulyoved avatar scamden avatar tyler-mitchell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-ts-form's Issues

How to access form state in a custom form?

Is there any way to access the formState in a custom form implementation? We want to display some kind of loading spinner when the form is submitting.

When i'm in a field, i can easily use useTsController but this is only allowed within fields, not in forms.

Composition Api

Is your feature request related to a problem? Please describe.
The concept of this library is very similar to my own implementation.
I've opted for a composition Api rather than prop based.
Would you be interested in discussing it?

Describe the solution you'd like
Composition Api vs prop Api.

issue: Array Type Matching

Version Number

1.0.6

Codesandbox/Expo snack

No response

Steps to reproduce

Create multiple array schemas with different base types:

const mapping = [
  [z.string().array(), StringArrayField],
  [z.number().array(), NumberArrayField],
] as const

With the current implementation, they would each map to the StringArrayField which is unexpected.

Expected behaviour

When checking for equality, the package should consider the arrays inner type.

Relevant log output

No response

Quick placeholders can't contain "//"

Version Number

^1.0.10

Codesandbox/Expo snack

No response

Steps to reproduce

  1. Create a schema like the following and pass it to the form
const schema = z.object({
    url: z.string().describe('URL // https://google.com'),
})

Expected behaviour

The placeholder should be https://google.com but it's only https:. I guess this is because the separator is // and so the label is segments[0] and the placeholder segments[1].

Relevant log output

No response

Schema created with 'createUniqueFieldSchema' stops working if added .optional()

I have the same issue reported on #50, I'm working with v1.2.0... I will add a code sandbox that reproduces the error.

Code sandbox that reproduces the error with minimal setup.
https://codesandbox.io/embed/react-ts-form-optional-error-3oz8lk?fontsize=14&hidenavigation=1&theme=dark


Code example, just in case.

const MAX_FILE_SIZE = 1000000000000;

export const FileSchema = createUniqueFieldSchema(
  z
    .any()
    .refine((value) => !!value && !!value.file, "Upload a file is required.")
    .refine(
      (value) => value?.file?.size <= MAX_FILE_SIZE,
      `Max file size is 5MB.`
    ),
  "FileSchema"
);
const FileSchemaComponent = () => <div>{`FileSchema :)`}</div>;

// create the mapping
const mapping = [[FileSchema, FileSchemaComponent]] as const; // ๐Ÿ‘ˆ `as const` is necessary

// A typesafe React component
const MyForm = createTsForm(mapping);

// Creates Schema
const schema = z.object({
  file: FileSchema.optional() // if .optional is added to a schema it stops working
  // My Original file field has a description, just in case I let an example without it, the issue persists in both cases
  // fileWithDescript: FileSchema.optional().description('Label // Description') // if .optional is added to a schema it stops working
});

Originally posted by @FedeMadoery in #50 (comment)

Typesafe props not working for field that is nullable.optional or .nullish

Prop types seems to break when optional/nullable/nullish are not in the correct order. See the reprodcution below

TL;DR:

  • optional().nullable() works
  • nullable().optional() does not
  • nullish() does not, as it is equivalent to nullable().optional()
import { TextInput } from "@mantine/core";
import { createTsForm, useTsController } from "@ts-react/form";
import { z } from "zod";

const TextField = ({ description }: { description?: string }) => {
  const { field, error } = useTsController<string>();

  return (
    <TextInput
      value={field.value}
      onChange={(e) => field.onChange(e.target.value)}
      label={field.name}
      placeholder={description}
      error={error?.errorMessage}
    />
  );
};

const mapping = [[z.string(), TextField]] as const;

export const Form = createTsForm(mapping);

export const MyFormComponent = () => {
  return (
    <Form
      schema={z.object({
        displayNameOptionalNullable: z.string().optional().nullable(),
        displayNameNullableOptional: z.string().nullable().optional(),
        displayNameNullish: z.string().nullish(),
      })}
      props={{
        displayNameOptionalNullable: {
          // Works, no TS errors
          description: "",
        },
        displayNameNullableOptional: {
          // Does not work, Type '{ description: string; }' is not assignable to type 'undefined'
          description: "",
        },
        displayNameNullish: {
          // Does not work, Type '{ description: string; }' is not assignable to type 'undefined'
          description: "",
        },
      }}
      onSubmit={() => {
        /* ... */
      }}
    />
  );
};

Feature: onInvalidSubmit, other callbacks?

Would it be useful to have other callback functions on the form component like onInvalidSubmit, onDirtyChange, etc?

Feel free to comment with any callbacks you think might be useful with a description of which callback functions you might want to see added to the form component.

ZodEffects support

Library doesn't support .transform on schemas currently:

const mapping = [[z.string(), TextField]] as const;

const Form = createTsForm(mapping);

const FormSchema = z.object({textField: z.string().tranform(v=>parseInt(v));

<Form schema={FormSchema} /> // Errors no found schema

Would be nice to be able to transform / preprocess stuff in the schema and still have it match the schema.

Array support

Couldn't find anything about dealing with arrays in the docs. I guess there needs to be native useFieldArray support?

Container around multiple sections of your form

From the limitations section:

@ts-react/form allows you to pass props to your components and render elements in between your components, which is good for almost all form designs out there. Some designs may not be easily achievable. For example, if you need a container around multiple sections of your form, this library doesn't allow splitting child components into containers at the moment. (Though if it's a common-enough use case and you'd like to see it added, open an issue!)

Not sure what will be the right approach but I like to be declarative as possible

For now, I experimented with this approach, not sure I like the DX, too much magic?

Passing some components to beforeElement, like section header GridColumnHeader
image

before layout the childrens, do some Children.toArray(children) tricks to split them based on the beforeElement and plot each part as separate section

image

Not sure I like this approach but it seem to do the work

enum support alternative?

I noticed you deprecated enum support with the reasoning the strings() can do everything enums can. The problem I'm finding is that the values I get back from the form on submit have strings in their type instead of enums (as you'd expect). I'm using your workaround to pass select options to the component but what's the workaround to get the types to be correct?

Our GQL input mutations have actual enums so i'd love to be able to define a typed form that matches that type exactly. Is there an alternative solution I just haven't found?

Lower required version of React in peerDependencies

I'm wondering if it would be possible to lower the support versions of React in the peerDependencies. I would love to use the library, but my team is currently forced to use React 17, so installing requires us to use overrides in each project we want to add it to. It would be nice not to have to do this if possible.

If there's no concerns, I'm happy to open a PR to change this to match the range currently supported by react-hook-form ("^16.8.0 || ^17 || ^18") https://github.com/react-hook-form/react-hook-form/blob/master/package.json#L122

Schema created with 'createUniqueFieldSchema' stops working if added .optional()

// components/form.ts
import { createTsForm, createUniqueFieldSchema } from '@ts-react/form'
import { z } from 'zod'

import { NumberField } from './NumberField'
import { TextField } from './TextField'

export const StringSchema = createUniqueFieldSchema(z.string(), 'string')
export const NumberSchema = createUniqueFieldSchema(z.number(), 'number')

const mapping = [
  [StringSchema, TextField],
  [NumberSchema, NumberField],
] as const

export const Form = createTsForm(mapping)
// pages/index.tsx
import { z } from 'zod'

import { Form, NumberSchema, StringSchema } from '../components/form'

const schema = z.object({
  name: StringSchema.optional(), // if .optional is added to a schema it stops working
  age: NumberSchema,
})

export function Home() {
  return (
    <Form
      schema={schema}
      onSubmit={(v) => {
        console.log(v)
      }}
      formProps={{
        className: 'flex flex-col gap-4 px-4 max-w-5xl lg:grid lg:grid-cols-2',
      }}
    />
  )
}

issue: Duplicate id passed to createFieldSchema

Version Number

1.0.9

Codesandbox/Expo snack

No response

Steps to reproduce

Copy the code and run it on nextjs application

Expected behaviour

I am not sure if this is the expected behaviour for createUniqueFieldSchema but this code generates the error: Duplicate id passed to createFieldSchema: SelectMenu101

import { z } from "zod";
import { TextArea } from "./TextArea";
import { createTsForm, createUniqueFieldSchema } from "@ts-react/form";
import { DashboardLayout } from "./DashboardLayout";
import { SelectMenu } from "./SelectMenu";

const SelectMenuFieldSchema = createUniqueFieldSchema(
  z.string(),
  "SelectMenu101"
);

const mapping = [
  [z.string(), TextArea],
  [SelectMenuFieldSchema, SelectMenu],
] as const;

const GeneratorForm = createTsForm(mapping);

const ProposalSchema = z.object({
  credits: SelectMenuFieldSchema,
  product: z.string(),
  profession: z.string(),
});

export default function MyForm() {
  function onSubmit(data: z.infer<typeof ProposalSchema>) {
    console.log(data);
  }

  return (
    <DashboardLayout>
      <main className="p-4">
        <GeneratorForm
          schema={ProposalSchema}
          onSubmit={onSubmit}
          renderAfter={() => (
            <div className="pt-5">
              <div className="flex justify-end">
                <button
                  type="button"
                  className="rounded-md border border-gray-300 bg-white py-2 px-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
                >
                  Cancel
                </button>
                <button
                  type="submit"
                  className="ml-3 inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
                >
                  Generate
                </button>
              </div>
            </div>
          )}
          props={{
            credits: {
              label: "Available credits",
              options: ["5 credit", "1 credit", "10 credits"],
              defaultValue: "5 credit",
            },
            product: {
              label: "Whats your product or solution service about ? required",
              placeholder: "Product or solution service",
              helpText:
                "Write a few words about the product or service you are building.",
            },
            profession: {
              label: "What does your potential client do ? Profession required",
              placeholder: "Profession",
              helpText:
                "Write a few words about the profession of your potential customers.",
            },
          }}
        />
      </main>
    </DashboardLayout>
  );
}

Relevant log output

Server Error
Error: Duplicate id passed to createFieldSchema: SelectMenu101. Ensure that each id is only being used once and that createFieldSchema is only called at the top level.

Where does schema go when creating custom form component?

I created custom component that looks like this:

import { createTsForm } from "@ts-react/form"
import { TextField } from "./TextField"
import { z } from "zod"

interface Props {
  children: JSX.Element
  onSubmit: () => void
}

// const FindOrderSchema = z.object({
//   email: z.string().email("Enter a real email please."),
//   orderNumber: z.string(),
// })

function Form({ children, onSubmit }: Props) {
  return (
    <form onSubmit={onSubmit}>
      {children}
      <button type="submit">submit</button>
    </form>
  )
}

const mapping = [[z.string(), TextField]] as const
export const FindOrderForm = createTsForm(mapping, { FormComponent: Form })

Read https://github.com/iway1/react-ts-form#customizing-form-components

However I am confused, where does schema go in this case?

I cannot do this:

image

issue: Typesafe props don't work when combining refine(), transform() and createUniqueFieldSchema()

Version Number

1.0.10

function Select({ options }: { options: string[] }) {
// ...
}

export const SelectStringSchema = createUniqueFieldSchema(z.string(), "select");

const mapping = [
	[z.string(), TextInput],
	[z.number(), NumberInput],
	[z.boolean(), Checkbox],
	[SelectStringSchema, Select] as const,
] as const;

In this very simple example, we the Form component doesn't expect the options props from Select - there's no error when we don't pass it and we get an error if we do try to pass it.

Feature - Add "Hidden Values"

If we could pass hidden values as a prop to the form component, it would make it viable to share most if not all form schemas with trpc-like backends which could make synchronization between front end and backend very easy in cases where the backend is using zod schemas for input.

For example, imagine we had a "comment" form that created a comment for a post. The form only needs to include a text field in the UI, but the backend expects both the text of the comment and the ID of the post:

export const MyCommentSchema = z.object({
  text: z.string(),
  postId: z.string(),
})
import { MyCommentSchema } from 'schema'; // import schema from shared package / file
// trpc router
const trpcRouter = t.router({
  postComment: z.procedure.input(MyCommentSchema).mutation(/* do stuff */)
})

In our next.js page for example:

import { MyCommentSchema } from 'schema'; // import schema from shared package / file
// front end

const MyPage = ()=>{
  const mutation = api.postComment.useMutation()
  const {query: {postId}} = useRouter()
  return (
    <Form 
      schema={MyCommentSchema}
      onSubmit={mutation.mutate}
      hiddenValues={{
        postId: postId,
      }}
    />
  )
}

No component would be rendered for the postId field (since we don't want users to have to input hidden values), and the hidden value would get passed to the onSubmit callback.

Any additional changes to our shared schema would both add the field to the input of the trpc router, and also render a new field on the frontend. Enables fullstack typesafe form submission with a single source of truth.

If input is type="number" and the form has a default value it can't be erased (using example number input)

import { useTsController } from '@ts-react/form'

export function NumberField({ req }: { req: number }) {
  const {
    field: { onChange, value },
    error,
  } = useTsController<number>()

  console.log('value: ', value) // if default value is 1 it still logs 'value: 1' even when trying to erase the value

  return (
    <>
      <span>
        <span>{`req is ${req}`}</span>
        <input
          type="number"
          value={value !== undefined ? value + '' : ''}
          onChange={(e) => {
            const value = parseInt(e.target.value)
            if (isNaN(value)) onChange(undefined)
            else onChange(value)
          }}
        />
        {error && error.errorMessage}
      </span>
    </>
  )
}

How to add `id` prop to each of the form inputs

Is your feature request related to a problem? Please describe.

We are testing our react app and that requires the input element to have a certain id prop like emailAddress or orderNumber .

I read docs and could not find a way to add them using this library as you seem to only be able to pass props or id to the wrapper div that wraps over the inputs and not each individual input itself. Unless I am mistaken.

Describe the solution you'd like

Be able to add id prop to each individual input box.

How to pass custom props to TextField

My TextField looks like this:

interface Props {
  placeholder: string
}

export function TextField({ placeholder }: Props) {
  const { field, error } = useTsController<string>()
  console.log(placeholder)
  return (
    <>
      <StyledInput
        value={field.value ? field.value : ""}
        onChange={(e) => {
          field.onChange(e.target.value)
        }}
      />
      {error?.errorMessage && <span>{error?.errorMessage}</span>}
    </>
  )
}

const StyledInput = styled("input")`
  padding: 1rem;
  border-radius: 3.125rem;
  border: 1px solid rgba(185, 185, 185, 1);
  font-size: 1rem;
  box-sizing: border-box;
  font-weight: 500;
  ::placeholder,
  ::-webkit-input-placeholder {
    color: red;
  }
`

Essentially I want to be able to pass placeholder prop to it but I don't know how.

Because now my Field component is gotten like this:

const mapping = [[z.string(), TextField]] as const
const MyForm = createTsForm(mapping)

And in render I can't do this:

        <MyForm
          placeholder={"Enter your email and order number"}
          schema={FindOrderSchema}
          onSubmit={onSubmit}
          renderAfter={() => <button type="submit">Submit</button>}
        />
        ```

Unable to pass options to select field when inside of a complex field type such as an address.

I have an address field setup as a complex field. Within that field I have two select fields. 1 for the state/region and the other for the country code.

The issue I'm having is I cannot figure out how to pass options down to these fields. Not sure how to get the code below to appear right. md is not behaving with me today. Any idea how to pass the options down to each select field within the address schema?

this is my form.tsx

`
const mapping = [
[z.string(), TextInput],
[z.boolean(), Checkbox],
[z.number(), NumberInput],
[PasswordSchema, PasswordField] as const,
[PasswordWConfirmSchema, PasswordWConfirmInput] as const,
[AddressSchema, AddressInput] as const,
[SelectSchema, SelectField]
] as const;

  const MyForm = createTsForm(mapping);
  
  const SignUpSchema = z.object({
      firstName: z.string().describe("First Name"),
      lastName: z.string().describe("Last Name"),
      email: z.string().email("Enter a real email please.").describe("Email"), // renders TextField
      password: PasswordWConfirmSchema,
      address: AddressSchema,
  })
  export function Form() {
      function onSubmit(data: z.infer<typeof SignUpSchema>) {
          // gets typesafe data when form is submitted
          alert(JSON.stringify(data));
      }
  
      return (
          <MyForm
              schema={SignUpSchema}
              onSubmit={onSubmit}
              renderAfter={() => <button type="submit">Submit</button>}
              props={{
                  address: {
  
                  }
              }}
          />
      );
  }

`

My AddressInput.tsx

`
const AddressInput = () => {
const {
field: { value, onChange },
error,
} = useTsController<z.infer>();
const line1 = value?.line1;
const line2 = value?.line2;
const locale = value?.locale;
const region = value?.region;
const postalCode = value?.postalCode;
const countryCode = value?.countryCode;
return (



<label
style={{
fontSize: 16,
display: "flex",
gap: ".5rem",
marginBottom: ".5rem",
justifyContent: "flex-end",
}}
>
Street Address
<input
style={{ padding: "0.25rem" }}
type="text"
value={line1}
onChange={(e) => onChange({ ...value, line1: e.target.value })}
/>

{error?.line1 && <div style={{ color: "red" }}>{error.line1.errorMessage}
}
<label
style={{
fontSize: 16,
display: "flex",
gap: ".5rem",
marginBottom: ".5rem",
justifyContent: "flex-end",
}}
>
<input
style={{ padding: "0.25rem" }}
type="text"
value={line2}
onChange={(e) => onChange({ ...value, line2: e.target.value })}
/>

{error?.line2 && <div style={{ color: "red" }}>{error.line2.errorMessage}
}

              <label
                  style={{
                      fontSize: 16,
                      display: "flex",
                      gap: ".5rem",
                      marginBottom: ".5rem",
                      justifyContent: "flex-end",
                  }}
              >
                  City
                  <input
                      style={{ padding: "0.25rem" }}
                      type="text"
                      value={locale}
                      onChange={(e) => onChange({ ...value, locale: e.target.value })}
                  />
              </label>
              {error?.locale && <div style={{ color: "red" }}>{error.locale.errorMessage}</div>}
              <div style={{
                  display: "flex",
                  gap: ".5em",
  
              }}>
                  <div style={{ maxWidth: "30%" }}>
  
  
                  </div>
  
                  <div style={{ maxWidth: "70%" }}>
                      <label
                          style={{
                              fontSize: 16,
                              display: "flex",
                              gap: ".5em",
                              marginBottom: ".5rem",
                              justifyContent: "flex-end",
                          }}
                      >
                          Zip Code
                          <input
                              style={{ padding: "0.25rem", maxWidth: "50%", }}
                              type="text"
                              value={postalCode}
                              onChange={(e) => onChange({ ...value, postalCode: e.target.value })}
                          />
                      </label>
                      {error?.postalCode && <div style={{ color: "red" }}>{error.postalCode.errorMessage}</div>}
                  </div>
              </div>
  
          </div>
      )
  }
  
  export default AddressInput
  
  const zipcodeRegex = RegExp('^\d{ 5} -\d{ 4} |\d{ 5} | [A - Z]\d[A - Z]\d[A - Z]\d$')
  export const AddressSchema = createUniqueFieldSchema(z.object({
      line1: z.string().describe("Street Address"),
      line2: z.string().optional(),
      locale: z.string().describe("City"),
      region: SelectSchema.describe("State"),
      postalCode: z.string().regex(zipcodeRegex, "Please Enter a valid Zip Code.").describe("Zip Code"),
      countryCode: SelectSchema.describe("Country Code"),
  }), "address")

Selectfunction SelectField({ options }: { options: string[] }) {
const { field, error } = useTsController();
const label = useDescription();
return (
<>
<label
style={{
fontSize: 16,
display: "flex",
gap: ".5rem",
marginBottom: ".5rem",
justifyContent: "flex-end",
}}
>
{label.label}

            <select
                value={field.value ? field.value : "none"}
                onChange={(e) => {
                    field.onChange(e.target.value);
                }}
            >
                {!field.value && <option value="none">Please select...</option>}
                {options.map((e) => (
                    <option key={e} value={e}>
                        {e}
                    </option>
                ))}
            </select>
        </label>
        <span>{error?.errorMessage && error.errorMessage}</span>
    </>
);

}

export default SelectField

export const SelectSchema = createUniqueFieldSchema(z.string(), "DropdownSelect")`

Add playground example (codesandbox/stackblitz)

Is your feature request related to a problem? Please describe.
I'm always frustrated when can't see link to playground example to quickly open and try

Describe the solution you'd like
Add playground examples to codesandbox/stackblitz

Support For Unique Enums

Right now it isn't possible to have a unique enum field that works in any useful way.

I think the only way to implement this would be creating a new function specifically for creating unique enum fields, something like:

const RadioSchema = createUniqueEnumField('id');

const MyForm = z.object({
  radio: MyUniqueEnum.enum(["one", "two"])
})

Needs to be able to pass in options at the form level and only be passed to the mapping once per field component type.

Improve z.array(z.enum([])) and z.enum([]).array()

Hello, amazing lib. I see the field to improve it.

Currently, we have something like this:

const Colors = ["red", "green", "blue"] as const;

const FavoriteColor = z.enum(Colors).array(); // 
// const FavoriteColor = z.array().enum(Colors)

const mapping = [
  [FavoriteColor, MultiCheckbox],
] as const; 

const Schema = z.object({
  favoriteColor: FavoriteColor.describe("Favorite Color"),
});

const MultiCheckbox = (props: { options: string[] }) => {
  const { options } = props;
  ...
}

Const App = ()=> 
    <MyForm
      form={form}
      schema={Schema}
      props={{
        favoriteColor: {
          options: FavoriteColor._def.type.options,  
        },
      }}
    />

It works, but I want it to look like this!

const Colors = ["red", "green", "blue"] as const;

const FavoriteColor = z.enum(Colors).array(); // 
// const FavoriteColor = z.array().enum(Colors)

const mapping = [
  [FavoriteColor, MultiCheckbox],
] as const; 

const Schema = z.object({
  favoriteColor: FavoriteColor.describe("Favorite Color"),
});

const MultiCheckbox = () => {
  const options = useEnumValues();
  ...
}

Const App = ()=> 
    <MyForm
      form={form}
      schema={Schema}
    />

I added PR with a proposition for it.
Functionality + tests.

issue: npm ERR Could not resolve dependency

Version Number

1.6.3

Codesandbox/Expo snack

No response

Steps to reproduce

  1. npm install @ts-react/form
  2. npm install react-hook-form @hookform/resolvers
  3. npm update

Expected behaviour

No npm Errors.

Relevant log output

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: [email protected]
npm ERR! Found: @hookform/[email protected]
npm ERR! node_modules/@hookform/resolvers
npm ERR!   @hookform/resolvers@"^3.1.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @hookform/resolvers@"^2.8.0" from @ts-react/[email protected]
npm ERR! node_modules/@ts-react/form
npm ERR!   @ts-react/form@"^1.6.3" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!

how to handle dependent field props?

In some forms, field props are dependen on values from other fields.

How are we going to solve this?

this could be quiet tricky


A solution for now is creating a custom component, which is dependent and loading the formState from the context (when implemented).

This should be documented. :D

issue: Example NumberField component can't get subsequently updated by the reset function if field is empty

Version Number

1.4.5

Codesandbox/Expo snack

https://codesandbox.io/s/cocky-driscoll-0bpr67?file=/src/App.tsx

Steps to reproduce

  1. Go to provided code sandbox
  2. Click 'Reset to defaults' (works -> both fields get updated to their default values)
  3. Empty both fields
  4. Click 'Reset to defaults' (only the text field gets updated to it's default value )

Expected behaviour

Number fields should keep getting updated beyond the first call to reset.

Relevant log output

No response

Allow async .superRefine

Is your feature request related to a problem? Please describe.
I want to use asynchronous functions to validate my schemas (using .superRefine). Specifically, I want to check my backend to make sure that the username and email aren't already in use, like so:

const CreateUserSchema = z
  .object({
    username: z.string().describe("Username // username"), // renders TextField
    email: z
      .string()
      .email("Enter a real email please.")
      .describe("Email // [email protected]")
      .optional(), // renders TextField
    name: z.string().describe("Name // John Doe").optional(),
  })
  .superRefine(async (values, ctx) => {
    const zodEmail = z.string().email().safeParse(values.email);
    const [userByUsername, userByEmail] = await getUsernameAndEmail(values.username, zodEmail.data);
    if (userByUsername.user !== null) {
      ctx.addIssue({
        code: "custom",
        message: "Username already taken.",
        path: ["username"],
      });
    }
    if (userByEmail.user !== null) {
      ctx.addIssue({
        code: "custom",
        message: "Email already taken.",
        path: ["email"],
      });
    }
  });

Technically this works, but I have to adjust my .eslint.json to include "@typescript-eslint/no-misused-promises": "warn", which is undesirable.

Describe the solution you'd like
Initially I thought this was a bug in Zod. I opened an issue, and got a response that I might need to be using parseAsync. I think switching to that might be the solution (although I am admittedly not super familiar with the react-ts-form source code, so take that with a grain of salt).

Describe alternatives you've considered
For now, I'm just leaving my "@typescript-eslint/no-misused-promises": "warn" as is. Not ideal, but not progress-stopping either.

Additional context
Add any other context or screenshots about the feature request here.

`Warning: A component is changing an uncontrolled input to be controlled` error in NextJS after following readme

Version Number

^1.0.6

Steps to reproduce

Have repo here: https://github.com/nikitavoloboev/test/tree/main/next-13

Can run pnpm i and pnpm run dev.

Actual code with inputs here: https://github.com/nikitavoloboev/test/blob/main/next-13/pages/index.tsx

I am not sure what I am doing wrong but every time I try to write in the input boxes I get this error:

image

I expanded on code that is in readme, perhaps I missed something? Repo was started from latest Next 13 starter.

Thank you.

Expected behaviour

No warning in console.

Relevant log output

A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.

Getting error following tutorial

My code:

import Card from "../../components/Card"
import Container from "../../components/Container"
import { z } from "zod"
import { TextField } from "../../components/TextField"
import { createTsForm } from "@ts-react/form"

const mapping = [[z.string(), TextField]] as const

const MyForm = createTsForm(mapping)

export default function SearchPage() {
  const FindOrderSchema = z.object({
    email: z.string().email("Enter a real email please."),
    orderNumber: z.string(),
  })

  function onSubmit(data: z.infer<typeof FindOrderSchema>) {
    // gets typesafe data when form is submitted
  }

  return (
    <>
      <Container>
        <Card
          title="Exchange or return your order"
          subtitle="Enter your details to find your order"
        >
          <MyForm
            schema={FindOrderSchema}
            onSubmit={onSubmit}
            renderAfter={() => <button type="submit">Submit</button>}
          />
        </Card>
      </Container>
    </>
  )
}

Getting this error though trying to open the page.

image

Not sure why. My TextField import looks like this:

import { useTsController } from "@ts-react/form"

export function TextField() {
  const { field, error } = useTsController<string>()
  return (
    <>
      <input
        value={field.value}
        onChange={(e) => {
          field.onChange(e.target.value)
        }}
      />
      {error?.errorMessage && <span>{error?.errorMessage}</span>}
    </>
  )
}

Docs: Field Type examples

Would be great to have an example implementation of every common type of field to get people started:

[x] text
[x] number
[x] dropdown select
[x] multi checkbox
[x] checkbox

Feel free to comment with any other examples that you would want to see in the field examples

issue: Accessing form state disables default values

Version Number

v.1.4.2

Codesandbox/Expo snack

https://codesandbox.io/s/black-mountain-14xil1?file=/src/App.tsx

Steps to reproduce

  1. Default value 'hello' appears in text field.
  2. Go to line 35 - (where you pass the result of useForm() to your form), uncomment the line and refresh.
  3. Default value doesn't appear in the text field anymore.

Expected behaviour

Default values passed into the form should appear even when form state is accessed.

Relevant log output

No response

Recursive generation for object types

Please tell me if I've missed this but. I think it would be really great to be able to represent a nested schema and have zod automatically walk the objects and render any primitive types if i don't have an z.object() mapping setup.

z.object({
  nested : z.object({
    numField : z.number()
  })
})

would just render whatever is mapped to the number editor and supply default values from the object path and recreate the object on edit automatically so values would be {nested : {numField : 9}}

P.S. We are considering using this very heavily and i'd be happy to contribute if you're open to some of these ideas. (I wrote a custom form gen library at my last job, so I think i have some relevant background that could be useful)

Form prop in FormContainer not available. When passing a react-hook-form form to the FormContainer, the form property is not available. This is making the library difficult to use, becuase its hard to access the submitting value of the form.

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

expose the schema to the mapped component

My use case is to show an asterisk next to required fields in say my TextField component. I'd like to be able to introspect the schema to see if it is optional / nullable or not. (Maybe there's a better answer than that. If so, would love to hear it)

Accessing the submitter element? (multiple submit buttons)

Question/Feature Request: Accessing the submitter element (button)

Hello. Sorry if this is specified on the docs or other issue, I wasn't able to find it by looking for 'submitter' or 'submit', etc. Is there a way to access the submitter element from the onSubmit function?. For example:

In the case of having multiple submit buttons for different actions with the same form state:

return (
...
<ul className="dropdown-menu dropdown-menu-dark bg-dark border-primary">
  {
      ['save', 'save as'].map((name) => (
          <li key={name}>
              <button
                  type="submit"
                  name={name}
                  value={name}
                  className="dropdown-item dropdown-item-dark"
              >{name}</button>
          </li>
          )
      )
  }
</ul>
...
)

The current way to get the event data is to store outside of the form component

export function FormContainer({
    id,
    onSubmit,
    children,
    header,
}: {
    id: string;
    onSubmit: () => void;
    children: ReactNode;
    header: string;
}) {
    const { action } = useAction();
    const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        action.name = ((event.nativeEvent as SubmitEvent).submitter as HTMLInputElement).name;
        onSubmit();
    }
    ...

And then the parent component can use the submitter value:

...
    const { action } = useAction();
    const submitPreset = (data: z.infer<typeof forms.preset>) => {
        console.log(action);
        console.log(data);
    };
...

It doesn't seem ideal, is the event available to the Form's parent element via any callback or form state? Can I modify the callback to include it? How could someone go about supporting multiple submit options? Haven't been able to find info. about it in the docs.

Thank you, very good library :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.