npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

formy

v1.0.15

Published

Formy is a form generation library in React. Create your form as a JS object and render it however you want.

Downloads

39

Readme

Formy

Formy is a form generation library in React. Create your form as a JS object and render it however you want.

Comes with helper functions for input events to maintain internal state.

Benefits

⛓   Total separation of data and layout

We were tired of input attributes getting mixed in with the HTML markup of a form.

Declare an input's state as a simple JS object and free up your HTML for what it's best for: layout.

A text input is now <Form.Field/>. A dropdown with a million options is now <Form.Field/>. Formy abstracts all markup differences, allowing you to write unified and simple templates.

✅   Native validation

We didn't write a bunch of crappy regex. Browsers back to IE10 can validate any input type and standard validation constraint. Declare your constraints up front and let the browser do all the work.

🔐   Custom validation

Create your own form constraints and validation messages just as easily as the built-in ones, built off the standardized setCustomValidity api.

Simple example

Create an object of your form's initial state.

const form = {
   id: 'signupForm',
   onSubmit: Form.onSubmitFactory(data => console.log(data)),
   fields: Form.fields({
      onChange: Form.onChangeFactory(form => this.setState({ form })),
   }, {
      name: {
         type: 'text',
         label: 'Name',
      },
      email: {
         type: 'email',
         label: 'Email',
      },
      password: {
         type: 'password',
         label: 'Password',
      },
      newsletterSignup: {
         type: 'checkbox',
         label: 'Signup for our newsletter?',
      },
   }),
};

this.state = { form };

Render the form.

const form = Form.getProps(this.state.form);

return(
   <Form.Component {...form}>
      <Form.Field {...form.fields.name}/>
      <Form.Field {...form.fields.email}/>
      <Form.Field {...form.fields.password}/>
      <Form.Field {...form.fields.newsletterSignup}/>
   </Form.Component>
);

HTML output:

<form id="signupForm">
   <label> Name <input type="text" name="name"> </label>

   <label> Email <input type="email" name="email"> </label>

   <label> Password <input type="password" name="password"> </label>

   <label>
      Signup for our newsletter?
      <input type="checkbox" name="newsletterSignup">
   </label>
</form>

Harder examples

In Formy you can define a field property's value as a computed function to resolve on render.

const form = {
   fields: Form.fields({
      onChange: Form.onChangeFactory(form => this.updateForm(form)),
   }, {
      newsletterSignup: {
         type: 'checkbox',
         label: 'Signup for our newsletter?',
      },
      email: {
         type: 'email',
         label: 'Email',
         disabled: form => !form.newsletterSignup.checked,
      },
   }),
};

In this example, the email address input is disabled only if the checkbox isn't checked. Normally to achieve this you would need to add javascript outside of a form's HTML markup. However, you now have two sources of form state: your declarative form data written as HTML attributes and your imperative form data written in JS as hooks from input events.

Formy combines computed values and static values all in the same initial form object, keeping your data contained and easy to understand.

To create a computed value, pass in a function as a field property's value. On render, Formy calls the function and passes in the current form object and fieldKey string. This allows you to return a rendered value relative to all available data in the form.

Group radio buttons as an array in the radios property of a RadioGroup object. In this example, 'burrito' is the default selected value.

const form = {
   id: 'thingsYouLike',
   fields: Form.fields({
      onChange: Form.onChangeFactory(form => this.updateForm(form)),
   }, {
      faveFood: {
         type: 'radiogroup',
         value: 'burrito',
         radios: [
            { label: 'Burrito', value: 'burrito' },
            { label: 'Pasta', value: 'pasta' },
         ],
      },
   }),
};

Render the RadioGroup as a single component.

const form = Form.getProps(this.state.form);

return(
   <Form.Component {...form}>
      <Form.Field {...form.fields.faveFood}/>
   </Form.Component>
);

This groups the radio buttons in a fieldset element, rendering the radio buttons in the order they're declared in the initial radios array.

<form name="signupForm">
   <fieldset>
      <label>
         Burrito
         <input type="radio" value="burrito" name="faveFood">
      </label>

      <label>
         Pasta
         <input type="radio" value="pasta" name="faveFood">
      </label>
   </fieldset>
</form>

Custom components are necessary for customizing a form beyond the default styles.

When a field is rendered, it's component is retrieved by accessing its componentLibrary property and retrieving the component associated with its type property.

You can retrieve a Form.Field's default component library like this:

Form.Field.defaultProps.componentLibrary

Here's an example of a custom component library extending Formy's default component library:

const customComponentLibrary = {
   ...Form.Field.defaultProps.componentLibrary,
   ...{
      text: props => (
         <label>
            <em>{props.label}</em>
            <input
               type={props.type}
               checked={props.checked}
               value={props.value}
               name={props.name}
               disabled={props.disabled}
               required={props.required}
               placeholder={props.placeholder}
               onChange={({ target: { value } }) => props.onChange({ value })}
            />
         </label>
      ),
   },
};

You can add a default componentLibrary property to every field in a form with the Form.fields function:

const form = {
   onSubmit: Form.onSubmitFactory(data => this.submitForm(data)),
   fields: Form.fields({
      onChange: Form.onChangeFactory(form => this.setState({ form })),
      componentLibrary: customComponentLibrary,
   }, {
      text: {
         type: 'text',
         label: 'Whoah this is a seriously crazy custom component',
      },
      checkbox: {
         type: 'checkbox',
         label: 'This is a default component',
      },
   }),
};

If you have a super special field that you want to render with a custom component, while not setting a whole new component library for all fields, you can add the componentLibrary property to a specific field object in the Form.fields function:

const form = {
   onSubmit: Form.onSubmitFactory(data => this.submitForm(data)),
   fields: Form.fields({
      onChange: Form.onChangeFactory(form => this.setState({ form })),
   }, {
      text: {
         type: 'text',
         label: 'Whoah this is a seriously crazy custom component',
         componentLibrary: customComponentLibrary,
      },
      checkbox: {
         type: 'checkbox',
         label: 'This is a default component',
      },
   },
};

Adding custom validation to your form fields follows this simple model:

  1. Declare your constraint. Ex: This input can't start with the letter 'z'.

  2. Add a validation message. Ex: "Names can't start with a 'z' sorry."

In Formy, custom validation looks like this:

const form = {
   fields: Form.fields({
      onChange: Form.onChangeFactory(form => this.setState({ form })),
   }, {
      name: {
         type: 'text',
         label: 'Enter your name',
         customValidity: Form.customValidityFactory(
            form => form.fields.name.value[0] !== 'z',
            "Names can't start with a 'z' sorry.",
         ),
      },
   }),
};

Your constraint function is just like all other computed properties. On render, Formy calls the function and passes in the current form object and fieldKey string, resolving to either an empty string (if valid) or the passed in validation message (if invalid).

You can stack built-in constraints with your custom constraints, so a field can have both be required and have to start with the letter 'z' like this:

{
   type: 'text',
   label: 'Enter your name',
   required: true,
   customValidity: Form.customValidityFactory(
      form => form.fields.name.value[0] !== 'z',
      "Names can't start with a 'z' sorry.",
   ),
}

Formy uses browser-native validation messages for its error states. If you want tighter control of your app's copy, you can override the standard validation messages by reimplementing native constraints as a customValidity function:

{
   type: 'text',
   label: 'Enter your name',
   // required: true, (reimplementing below)
   customValidity: Form.customValidityFactory(
      form => form.fields.name.value,
      "This field is required",
   ),
}

Form properties

A form object can have these properties:

Note: You can make any property a function that resolves to the appropriate type on render. See the "Computed properties" example above.

| Name | Type | Description | | - | - | - | | fields | Object | An object of form fields | | id | String | The id attribute of a form | | name | String | The name attribute of a form | | onSubmit | function | Function to hook to a form's onsubmit event. |

Field properties

A field object can have these properties:

Note: You can make any property a function that resolves to the appropriate type on render. See the "Computed properties" example above.

Core properties

| Name | Type | Default | Description | | - | - | - | - | | checked | Boolean | false | The checked value of a field. | | componentLibrary | Object | FormDefaultComponentLibrary | Object of react components to render form fields, with properties corresponding to all available type values. | | name | String | The field object's key | The name value of a field. Defaults to the field object's key in the Form.fields function. | | type | String | 'text' | The type of field to render. Available default types: 'text', 'email', 'password', 'number', 'textarea', 'checkbox', 'radio', 'radiogroup'. Soon to be added: 'select'. | | value | String | '' | The value of a field. |

Supported properties

| Name | Type | Description | | - | - | - | | autocomplete | String | The autocomplete value of a field. | | customValidity | String | The custom validation message of a field. An empty string means it's valid. A non-empty string means it's invalid. | | disabled | Boolean | The disabled value of a field. | | label | String | The label value of a field. | | max | String OR Number | Constraint value for the max attribute | | maxLength | Non-negative integer | Constraint value for the maxlength attribute | | min | String OR Number | Constraint value for the min attribute | | minLength | Non-negative integer | Constraint value for the minlength attribute | | onBlur | Function | Function to hook to a field's onBlur event. | | onChange | Function | Function to hook to a field's onChange event. | | onFocus | Function | Function to hook to a field's onFocus event. | | onInvalid | Function | Function to hook to a field's onInvalid event. | | onMouseEnter | Function | Function to hook to a field's onMouseEnter event. | | onMouseLeave | Function | Function to hook to a field's onMouseLeave event. | | pattern | String | Constraint value for the pattern attribute | | placeholder | String | An input's placeholder value. | | radios | Array | An array of field objects to populate a radiogroup field. The type value of these radio objects doesn't need to be set since it's assumed to be radio. | | required | Boolean | Constraint value for the required attribute. Not applicable for a radiogroup field. | | rows | Positive integer | The rows value of a textarea. Not valid for any other field. | | step | Number or 'any' | Constraint value for the step attribute |

Other properties

You are welcome to add any properties you want to a Form or Field object – they're just objects! The only downside is they won't be type checked like the core or supported properties. Functions will be executed just like all computed properties.

API


Form

Library wrapper object.


Form.Component

Top level form component.

Props

A Form.getProps return value.

Returns

<form
   name={props.name}
   onSubmit={props.onSubmit}
>
   {props.children}
</form>

Form.Field

Container component used to structure a form.

Props

A field object of a Form.getProps return value.

Returns

<props.componentLibrary[props.type] {...props}/>

Form.customValidityFactory

Factory function for creating a custom validation message.

Parameters

| Name | Type | Default value | Description | | - | - | - | - | | constraint | Function | none | Your custom validation logic. Passes in the current form object and fieldKey string, and expects a Boolean return value. true means valid, false means invalid. | validationMessage | String | 'Invalid' | The validation message to display if the custom validation is invalid.

Returns

| Name | Type | Description | | - | - | - | | customValidity | String | The custom validity message. An empty string if valid, and validationMessage if invalid.


Form.fields

Helper function to generate an object of fields.

Parameters

| Name | Type | Description | | - | - | - | | globals | Object | Object of values to assign to every field | fields | Object | Object of fields

Returns

| Name | Type | Description | | - | - | - | | fields | Object | The fields object, with every field now containing all the default field values, globals values, as well a name value with the value being the field object's key.

Example

Form.fields({
   onChange: event => {},
}, {
   firstName: {},
   lastName: {},
})

/*
{
   firstName: {
      checked: false,
      componentLibrary: {...},
      name: 'firstName',
      onChange: event => {},
      type: 'text',
      value: '',
   },
   lastName: {
      checked: false,
      componentLibrary: {...},
      name: 'lastName',
      onChange: event => {},
      type: 'text',
      value: '',
   },
}
*/

Form.getData

Function to get a form's data to be submitted.

Parameters

| Name | Type | Description | | - | - | - | | form | Object | Form state


Form.getProps

Function to get a form's props for rendering.

Parameters

| Name | Type | Description | | - | - | - | | form | Object | Form state


Form.onChangeFactory

Factory function to hook into an input's onChange event.

Parameters

| Name | Type | Description | | - | - | - | | callbackFn | Function | Function to call in an onChange event. When called, it passes in the new form state object as a parameter.


Form.onSubmitFactory

Factory function to hook into a form's submit event. Cancels the native submit event.

Parameters

| Name | Type | Description | | - | - | - | | callbackFn | Function | Function to call in a submit event. When called, it passes in the form's data object as a parameter.