React Form Typescript Plugin
See original GitHub issueForms are the majority of the front end work in a lot of applications. Graphql Schemas provide everything we need to generate the form.
Some tooling exists for doing similar things. EG: uniform.tools and a few other dead packages that attempt to do this at runtime.
IMO generating the forms would be a much better approach.
Formik is a very popular react form library with support for typescript, validation, and nested objects.
A form generation plugin is tricky because it needs to be customizable enough to suit peoples needs, with regards to styles and implementations of inputs, as well as validation, and hidden elements. The way formik is built provides an easy (ish) path to code generation, because it uses context for values, change handlers, metadata, etc…
The plugin should generate a “base” input for each built in scalar. Then Input Object types, and lists can be recursively composed from the base inputs.
Lists should be their own component as well, with an delete for each button in the list, and an add button at the end.
Circular references for Input Types can be handled by an Add <InputTypeName>
.
Customization could be handled by generating a context Provider, which allows over rides for scalar types.
EG of a simple output
mutation myMutation (username: String!, $id: ID!) {
updateMyUserName(username: $username, id: $id) {
username
id
}
}
// generatedCode.tsx
import React, { createContext, useContext } from 'React'
export const StringInput = (originalProps) => {
const inputContext = useContext(myMutationContext);
const { label, ...props } = originalProps;
const [field, meta] = useField(props);
if(inputContext.String) return <inputContext.String {...originalProps} />
return (
<>
<label htmlFor={props.id || props.name}>{label}</label>
<input className="text-input" {...field} {...props} />
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
};
export const IDInput = (originalProps) => {
const inputContext = useContext(myMutationContext);
const { label, ...props } = originalProps;
const [field, meta] = useField(props);
if(inputContext.ID) return <inputContext.ID {...originalProps} />
return (
<>
<label htmlFor={props.id || props.name}>{label}</label>
<input className="text-input" type="number" {...field} {...props} />
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
};
export const myMutationContext = createContext<{[key: ScalarNames]: React.Component}>({})
export const MyMutationForm = (props: {onSubmit: () => unknown } ) =>{
return (
<Formik onSubmit={onSubmit}>
<Form>
<StringInput name="username" />
<IntInput name="id" />
</Form>
</Formik>
)
}
example usage
import React from 'react'
import {myMutationContext , MyMutationForm } from './generated'
export const MyComponent () {
return (
<myMutationContext.Provider context={{
ID: MyCustomIDLookupComponent
}} >
<MyMutationForm onSubmit={(formValues) => console.log({formValues}) } />
</myMutationContext.Provider />
)
}
Additionally, default types, and basic yup validations could also be generated.
Issue Analytics
- State:
- Created 2 years ago
- Comments:11 (1 by maintainers)
Validation took FOR EV ER. Lots of edge cases and small things to deal with. But I think it’s basically done, there might be some small edge cases that I missed, but the api feels good, and handles all the cases I can think of.
Cases:
If you can think of any other cases, please LMK.
Next steps
For the more verbose update: https://twitter.com/ericwooley/status/1399191213501153289
as far as the validation API, here is what validation will look like for now: https://github.com/ericwooley/graphql-code-generator-react-form/blob/main/examples/components/addUser.tsx#L14 @mmahalwy @brabeji LMK what you think.
And here is a customized input that shows the errors: https://github.com/ericwooley/graphql-code-generator-react-form/blob/main/examples/components/addUser.tsx#L43
You can see it in action on the first form here: https://graphql-code-generator-react-form.thewooleyway.com/
It’s not complete yet, so validation does not yet block form submission, and there is no validation setup on the complex forms yet, but it should work, according to typescript.