{"id":18817309,"url":"https://github.com/blueberryapps/onion-form","last_synced_at":"2025-04-13T23:01:22.975Z","repository":{"id":40617294,"uuid":"72187697","full_name":"blueberryapps/onion-form","owner":"blueberryapps","description":"React Redux form builder with great UX validations","archived":false,"fork":false,"pushed_at":"2023-01-25T23:35:53.000Z","size":855,"stargazers_count":49,"open_issues_count":18,"forks_count":1,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-03-27T13:12:43.862Z","etag":null,"topics":["blueberry-opensource","form","javascript","react","redux"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blueberryapps.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-28T08:12:01.000Z","updated_at":"2024-02-05T13:12:21.000Z","dependencies_parsed_at":"2023-02-14T12:31:18.831Z","dependency_job_id":null,"html_url":"https://github.com/blueberryapps/onion-form","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blueberryapps%2Fonion-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blueberryapps%2Fonion-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blueberryapps%2Fonion-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blueberryapps%2Fonion-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blueberryapps","download_url":"https://codeload.github.com/blueberryapps/onion-form/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248161287,"owners_count":21057556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blueberry-opensource","form","javascript","react","redux"],"created_at":"2024-11-08T00:10:49.366Z","updated_at":"2025-04-13T23:01:22.927Z","avatar_url":"https://github.com/blueberryapps.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Onion Form](https://raw.githubusercontent.com/blueberryapps/onion-form/master/onion-form.png)\n[![CircleCI](https://circleci.com/gh/blueberryapps/onion-form.svg?style=svg\u0026circle-token=354f9bfd4c09ed529e4ff20019fc6668d03d9aa1)](https://circleci.com/gh/blueberryapps/onion-form) [![Dependency Status](https://dependencyci.com/github/blueberryapps/onion-form/badge)](https://dependencyci.com/github/blueberryapps/onion-form)\n\n\u003e As a developer you are assigned with creating a registration form on Registration page\n\u003e with fields for first name, last name, e-mail and password, validate them and then\n\u003e send all these fields to API. Not again? This package will make your life easier by simplifying the dealing with forms.\n\n```\nyarn add --save onion-form\n```\n\nThis package is only meant to be used together with Redux!\n\n## TLDR\n\n```javascript\nimport { Form, Field, Submit } from 'onion-form';\n\n\u003cForm\n  name=\"signIn\"\n  onError={({ errors }) =\u003e { console.log(errors) }}\n  onSubmit={({ values }) =\u003e { console.log(values) }}\n  validations={{ email: (value) =\u003e [((value \u0026\u0026 !value.match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i)) ? 'wrongFormat' : null)]}}\n\u003e\n  \u003cField name='email' type='email' /\u003e\n  \u003cField name='password' type='password' /\u003e\n  \u003cSubmit\u003eSign In\u003c/Submit\u003e\n\u003c/Form\u003e\n```\n\n## Usage\n\n```javascript\n// Registration.react.js\nimport React, { Component } from 'react';\nimport { Form, Submit, connectField } from 'onion-form';\n\n// validations\nconst isRequired = (value) =\u003e ((!value) ? 'required' : null);\nconst emailNeedsToContainName = (_, otherValues) =\u003e ((!otherValues.email || otherValues.email.indexOf(otherValues.name) === -1) ? 'invalidEmail' : null);\nconst needLetters = (value) =\u003e (value \u0026\u0026 !value.match(/[a-zA-Z]+/i ? 'needLetters' : null);\nconst needNumbers = (value) =\u003e (value \u0026\u0026 !value.match(/\\d+/i) ? 'needNumbers' : null);\n\nconst validations = {\n  lastName: [isRequired],\n  email: [emailNeedsToContainName],\n  password: [needLetters, needNumbers]\n};\n\n// You need to have a component which will receive all data by props\n// error, hint, label, name, onBlur, onChange, onFocus, onionFormName, tooltip\nconst BasicInput = (props) =\u003e (\u003cinput type=\"text\" {...props} /\u003e);\n\n// Create your fields (can be used in different forms)\nconst FirstName = connectField('firstName')(BasicInput);\nconst LastName  = connectField('lastName')(BasicInput);\nconst Email     = connectField('email', { type: 'email' })(BasicInput);\nconst Password  = connectField('password', { type: 'password' })(BasicInput);\n\nexport default class RegistrationPage extends Component {\n\n  onSubmit({ values: { firstName, lastName, email, password } }) {\n    // apiCall('POST', { firstName, lastName, email, password })\n  }\n\n  onError({ errors: { firstName, lastName, email, password } }) {\n    // alert, show flash message what ever you need to do when use tryies to\n    // submit form and gets validation errors\n  }\n\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003ch1\u003eRegistration\u003c/h1\u003e\n        \u003cForm\n          name=\"myRegistrationForm\"\n          onSubmit={this.onSubmit.bind(this)}\n          onError={this.onError.bind(this)}\n          validations={validations}\n        \u003e\n          \u003cFirstName label=\"Your first name\" /\u003e\n          \u003cLastName /\u003e\n          \u003cEmail /\u003e\n          \u003cPassword /\u003e\n          \u003cSubmit\u003eRegister\u003c/Submit\u003e\n        \u003c/Form\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n```\n\n### Validations\nThere are three ways how you can add validations to your form:\n\n1. Pass an object with validations to the `Form` component as props (see examples above)\n2. Pass an array of validations to the `connectField` function: `connectField('password', null, [isRequired(), password()])`\n3. Specify the validations when the field component is being used:\n\n```js\nexport default class MyForm extends Component {\n  render() {\n    return (\n      \u003cForm name=\"myForm\"\u003e\n        \u003cEmail validations={[isRequired(), email()]} /\u003e\n        \u003cPassword validations={[isRequired(), password()]}/\u003e\n        \u003cSubmit\u003eLogin\u003c/Submit\u003e\n      \u003c/Form\u003e\n    )\n  }\n}\n```\n\nAll validations you specify will be used.\n\n### Redux\n\n!You need to add onion form reducer to your reducers and it must be under `onionForm` first level key!\n\n```javascript\n// store.js\nimport { createStore, combineReducers } from 'redux';\nimport { reducer as onionForm } from 'onion-form';\n\nconst store = createStore(combineReducers({ onionForm }), {})\n```\n\n## Action Creators\n\nWe have multiple action creators for communication with reducer:\n`setMultipleFields`, `setFormFieldProperty`, `clearForm`, `clearFormProperty`, `setFieldValue`,\n`setFieldLiveValidation`, `setFieldError`, `setFieldApiError`\nAll these actions accept `formName` as the first parameter which needs to match FORM_NAME in `\u003cForm name=`FORM_NAME`/\u003e`.\n\n\u003e All connected fields get __formName__ from __context__.\n\nBut sometimes you need to communicate with fields from your code and repeating\nname of the form can be exhausting, so we provide `createFormActions(formName)`\nwhich returns all the actions with `formName` set.\n\n## connectField(FIELD_NAME, DEFAULT_PROPS)(DECORATED_COMPONENT)\n\n__DEFAULT_PROPS__:\ncan be a plain __{}__ or a function which receives props as\nthe first parameter and needs to return __{}__. This function gets resolves in render on every rerender.\n`(props) =\u003e ({ label: props.msg('key.to.label') })`\n\n__FIELD_VALUES_FROM_STATE__:\nBy default we store these values in redux state:\n```javascript\n{\n  value: '',\n  liveValidation: false,\n  error: null,\n  apiError: null\n}\n```\nBut you can use\n`setMultipleFields(form, property, values)` or\n`setFormFieldProperty(form, field, property, value)`\nto set custom properties which will be then passed to the decorated component as well.\n\n__ONION_PROPS:__\n`error`, `hint`, `label`, `name`, `onBlur`, `onChange`, `onFocus`, `onionFormName`, `tooltip`\n\nWhen you initialize a component in `render` you can pass the following PASSED_PROPS:\n\n__PASSED_PROPS__\n`label`, `onBlur`, `onFocus`, `onChange`, `tooltip`, `hint`, `defaultValue`\nThey will be transferred to the decorated component.\nFunctions passed by props (`onFocus`, `onChange`, `onBlur`) will get called too, after onion form callbacks.\n\nPassing order of props is: __DEFAULT_PROPS__ -\u003e __FIELD_VALUES_FROM_STATE__ -\u003e __ONION_PROPS__ -\u003e __PASSED_PROPS__\n\nYou can pass `defaultValue` to component by (PROPS or __DEFAULT_PROPS__) to set that value to state on componentDid mount when field has no value already set.\n\n## connectSubmit(DECORATED_COMPONENT)\n\nYou can use `connectSubmit` which will pass `onClick`, `valid`, `hasValues`, `hasErrors` and `disabled` as prop to the decorated component:\n\n```js\n// CustomSubmit.react.js\nimport { connectSubmit } from 'onion-form';\n\nconst Button = ({ children, disabled, onClick }) =\u003e (\n  \u003cbutton disabled={disabled} onClick={onClick} type=\"submit\"\u003e{children}\u003c/button\u003e\n);\n\nexport default const connectSubmit(Button);\n```\n\n- `onClick`: callback function for submitting form\n- `valid`: returns true/false based on fields validations runned against state (errors doesn't need to be in store)\n- `hasErrors`: returns true if form is invalid (based on state from Redux)\n\n## Translations\n\nYou need to pass to component function `msg('keypath') =\u003e string`.\n\nImplemetation is your thing but it needs to follow:\n```\nmsg('key.foo') // returns translation for key.foo\nmsg(['foo', 'bar']) // returns translation for foo if exists else bar\n```\n\nWe use this function to resolve translations for the\n`error`, `hint`, `label`, `tooltip`, `placeholder` props.\n\n__error__ is specific because we are trying to get text by:\n```javascript\nconst error = field.error || field.apiError;\nconst errorText = error\n  ? msg([`form.${formName}.errors.${error}`, `form.errors.${error}`, `errors.${error}`])\n  : '';\n```\n\nothers are easier, for example __label__:\n```javascript\nconst labelText = label || defaultProps.label || msg([`form.${formName}.${fieldName}.label`, `form.${fieldName}.label`, `${fieldName}.label`]);\n```\n\n# !For detailed documentation of all options do `yarn test`!\n\n## Commands\n* __yarn test__: runs mocha tests\n* __yarn test:watch__: runs mocha test with watch option\n* __yarn coverage__: create code coverage report\n\n## Made with love by\n[![](https://camo.githubusercontent.com/d88ee6842f3ff2be96d11488aa0d878793aa67cd/68747470733a2f2f7777772e676f6f676c652e636f6d2f612f626c75656265727279617070732e636f6d2f696d616765732f6c6f676f2e676966)](https://www.blueberry.io)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblueberryapps%2Fonion-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblueberryapps%2Fonion-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblueberryapps%2Fonion-form/lists"}