{"id":13437769,"url":"https://github.com/andrewhathaway/Winterfell","last_synced_at":"2025-03-19T18:30:37.490Z","repository":{"id":1109244,"uuid":"41263237","full_name":"andrewhathaway/Winterfell","owner":"andrewhathaway","description":"Generate complex, validated and extendable JSON-based forms in React.","archived":false,"fork":false,"pushed_at":"2022-12-01T22:41:37.000Z","size":1402,"stargazers_count":787,"open_issues_count":27,"forks_count":120,"subscribers_count":29,"default_branch":"master","last_synced_at":"2024-04-15T00:07:19.104Z","etag":null,"topics":["complex","customisable","forms","json","react","reactjs","validated","validation","winterfell"],"latest_commit_sha":null,"homepage":"http://winterfell.andrewhathaway.net","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andrewhathaway.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-23T19:11:53.000Z","updated_at":"2024-01-08T19:49:03.000Z","dependencies_parsed_at":"2023-01-13T10:56:57.143Z","dependency_job_id":null,"html_url":"https://github.com/andrewhathaway/Winterfell","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhathaway%2FWinterfell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhathaway%2FWinterfell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhathaway%2FWinterfell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewhathaway%2FWinterfell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrewhathaway","download_url":"https://codeload.github.com/andrewhathaway/Winterfell/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244483106,"owners_count":20460056,"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":["complex","customisable","forms","json","react","reactjs","validated","validation","winterfell"],"created_at":"2024-07-31T03:01:00.103Z","updated_at":"2025-03-19T18:30:37.188Z","avatar_url":"https://github.com/andrewhathaway.png","language":"JavaScript","funding_links":[],"categories":["Uncategorized","Code Design"],"sub_categories":["Uncategorized","Form Logic"],"readme":"# Winterfell\n\n**Generate complex, validated and extendable JSON-based forms in React**\n\nWinterfell allows you to build up complex, multi-page forms with conditional questions, validation and conditional-page switching via a JSON schema, rendered by React.\n\nWinterfell was initially made for a project in a sector that required a large, complex form with questions that would result in more questions or different pages when you clicked next. With an easy to write schema and a high level of customisation, comes a great power.\n\n[View Demo](http://winterfell.andrewhathaway.net) -\n[Follow me on Twitter](http://twitter.com/andrewhathaway)\n\n## Usage\n\nFirst install [Winterfell via npm](https://www.npmjs.com/package/winterfell)\n\n```bash\n$ npm install winterfell --save\n```\n\nWinterfell uses a JSON schema to render your form. We will go through that later.\n\n```javascript\nvar Winterfell = require('winterfell');\nvar schema     = require('./schema');\n\nReact.render(\n  \u003cWinterfell schema={schema} /\u003e,\n  document.getElementById('form')\n);\n```\n\n## Features\n\n- Easy, quick and extendable\n- JSON schema\n- Design agnostic and customisable\n- Multi-page forms\n- Infinitely-recursive conditional questions\n- Conditional page switching\n- Conditional form submitting\n- Disable regular submissions\n- Instant form validation\n- Decide when to validate per field\n- Validation against other fields values\n- Predefined validation types\n- Predefined error messages\n- Custom validation types\n- Custom error messages\n- Custom error rendering\n- Custom required asterisk rendering\n- Custom classes\n- Custom InputTypes\n- Question pre and post text\n- Question panel header and text\n- Question set header and text\n- Ability to disable buttons\n- Default values\n- Events\n\n## Schema\n\nThe schema is built up of three main parts, `formPanels`, `questionPanels` and `questionSets`.\n\n#### Form Panels\n\nThe initial `formPanels` entry is used as a page of questions, or `questionPanels` in Winterfell's case.\n\n```json\n{\n  \"formPanels\": [\n    {\n      \"index\": 1,\n      \"panelId\": \"intro-panel\"\n    },\n    {\n      \"index\": 2,\n      \"panelId\": \"register-panel\"\n    },\n    {\n      \"index\": 3,\n      \"panelId\": \"final-panel\"\n    }\n  ]\n}\n```\n\n#### Question Panels\n\nQuestion Panels are the fleshed-out details about a page of questions. We defined the `questionSets` that exist on this page, any conditions for submitting the panel and button information. You should have one of these for every panel defined in formPanels above.\n\nEach `questionPanel` has the ability to have a header and some text along with it that is displayed above the questions. You can define these via the `panelHeader` and `panelText` fields.\n\nSupported actions are `GOTO` and `SUBMIT`. When using `GOTO`, the `target` can be any `questionPanelId`. `SUBMIT` places the `target` in to the action field of the form element.\n\n```json\n{\n  \"questionPanels\": [\n    {\n      \"panelId\": \"intro-panel\",\n      \"panelHeader\": \"A quick survey?\",\n      \"panelText\": \"Please could you take a few minutes to fill out our survey?\",\n      \"action\": {\n        \"conditions\": [\n          {\n            \"questionId\": \"existing-user\",\n            \"value\": \"no\",\n            \"action\": \"GOTO\",\n            \"target\": \"register-panel\"\n          }\n        ],\n        \"default\": {\n          \"action\": \"GOTO\",\n          \"target\": \"final-panel\"\n        }\n      },\n      \"button\": {\n        \"text\": \"Next\",\n        \"disabled\": false\n      },\n      \"\": {\n        \"text\": \"Back\",\n        \"disable\": false\n      },\n      \"questionSets\": [\n        {\n          \"index\": 1,\n          \"questionSetId\": \"intro-set\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n#### Question Sets\n\nQuestions Sets are groups of questions. Here is where you define questions with their validations, types, conditions etc. `conditionalQuestions` are recursive and will work as expected.\n\nThe questionSet below has an initial radio button with `yes` and `no` options. When you select `yes`, a question asking for the users email address will render.\n\nEach question has the ability to have some `text` associated with it which gets rendered below the questions-label and some `postText` which will be rendered below the questions input.\n\n\n```json\n{\n  \"questionSets\": [\n    {\n      \"questionSetId\": \"intro-set\",\n      \"questionSetHeader\": \"I am a question set header\",\n      \"questionSetText\": \"I am a question set text\",\n      \"questions\": [\n        {\n          \"questionId\": \"existing-user\",\n          \"question\": \"Are you an existing user?\",\n          \"text\": \"We'd just like to know so we can get you in the right place.\",\n          \"input\": {\n            \"type\": \"radioOptionsInput\",\n            \"default\": \"yes\",\n            \"options\": [\n              {\n                \"text\": \"Yes\",\n                \"value\": \"yes\",\n                \"conditionalQuestions\": [\n                  {\n                    \"questionId\": \"register-user-email\",\n                    \"question\": \"Please enter the email address your account is registered with\",\n                    \"postText\": \"We will not spam your email address.\",\n                    \"input\": {\n                      \"type\": \"emailInput\",\n                      \"placeholder\": \"Email Address\"\n                    },\n                    \"validateOn\": \"blur\",\n                    \"validations\": [\n                      {\n                        \"type\": \"isLength\",\n                        \"params\": [\n                          1\n                        ]\n                      }\n                    ]\n                  }\n                ],\n                \"validations\": [\n                  {\n                    \"type\": \"isLength\",\n                    \"params\": [\n                      1\n                    ]\n                  }\n                ]\n              },\n              {\n                \"text\": \"No\",\n                \"value\": \"no\",\n                \"conditionalQuestions\": []\n              }\n            ]\n          }\n        }\n      ]\n    }\n  ]\n}\n```\n\nThe `validateOn` property is used to dictate when to validate the field. The default for this is `blur`, which results in the field being validated when the user unfocusses from the field. You can also set this field to `change` which will validate the field as the user types, or changes their answer. Setting `validateOn` to `submit` will result in the field being validated when the next or submit button being pressed and only then.\n\nValidations are handled via the [Validator](https://www.npmjs.com/package/validator) package on npm. In the `validations` key item, you can set your types of validation for the field. The `type` must be a method on the Validator package, or a custom defined method.\n\nA validation-items `params` key must be an array of parameters for the validation method. The value will be unshifted to the start of the array and called up on the validation method in order. For example:\n\nValidation item where the value must be a minimum length of 1.\n\n```json\n{\n  \"type\": \"isLength\",\n  \"params\": [\n    1\n  ]\n}\n```\n\nValidation item where the value must be a minimum length of 1 and a maximum of 20.\n\n```json\n{\n  \"type\": \"isLength\",\n  \"params\": [\n    1,\n    20\n  ]\n}\n```\n\nYou can also add a custom error message for the questions validaton item by using the `message` property.\n\n```json\n{\n  \"type\": \"isLength\",\n  \"params\": [\n    1\n  ],\n  \"message\": \"Please select an option\"\n}\n```\n\nTo validate a questions answer against another questions answer, you can wrap curly-braces around a parameter in the `params` property and it will be turned in to a questions answer. For example:\n\n\n```json\n{\n  \"type\": \"equals\",\n  \"params\": [\n    \"{password}\"\n  ],\n  \"message\": \"Confirm Password must match the Password field\"\n}\n```\n\n#### HTML Classes\n\nWinterfell allows you to define classes for the rendered form in multiple different areas. To use them, place them in the root of the form-schema like so:\n\n```\n{\n  \"formPanels\": [],\n  \"classes\": {\n    \"form\": \"form-wrapping-class\",\n    \"label\": \"question-label\"\n  }\n}\n```\n\nThe table below describes the current set of classes.\n\nClass Name | Description\n--- | ---\nform                         | The form element itself\nquestionPanels               | The div that wraps around the active `questionPanel`\nquestionPanel                | The div that wraps around the active `questionSets` and the button bar\nquestionPanelHeaderContainer | The div that wraps around the `questionPanels` header text and text\nquestionPanelHeaderText      | The h3 tag that holds the `questionPanel` header text\nquestionPanelText            | The p tag that holds the `questionPanel` text\nquestionSetHeader            | The h4 tag that holds the `questionSet` header\nquestionSetText              | The p tag that holds the `questionSet` text\nquestionSetHeaderContainer   | The div that wraps around the header and text of a `questionSet`\nquestionSets                 | The div that wraps around the `questionSets` inside of a `questionPanel`\nquestionSet                  | The div that wraps around the `questions` inside a `questionSet`\nquestion                     | The div that wraps around the `question`\nquestionText                 | The p tag that holds the `question` text\nquestionPostText             | The p tag that holds the `question` post-text\nlabel                        | Label inside of a `question`\nbackButton                   | Panel-back button, shown when on a second panel\ncontrolButton                | Typically the Next or Submit button, depending on panel\nbuttonBar                    | The div wrapped around the buttons described above\nerrorMessage                 | Error Message div class - Not used if custom renderError method used\ninput                        | Assigned to the inputs for types `textInput`, `textareaInput`, `emailInput` and `passwordInput`\nselect                       | Assigned to the `selectInput` select-element\nfile                         | Assigned to the `fileInput` file-element\ncheckboxInput                | The div that wraps around the `checkboxInput`\ncheckbox                     | Assigned to the `checkboxOptionsInput` and `checkboxInput` checkbox-input\ncheckboxList                 | Assigned the to UL wrapped around the checkbox items in `checkboxOptionsInput`\ncheckboxListItem             | Assigned to the LI inside of the `checkboxList` mentioned above\ncheckboxLabel                | Assigned to the label inside of a checkbox option\nradioList                    | Assigned to the UL wrapped around the radio items in `radioOptionsInput`\nradioListItem                | Assigned to the LI inside of the `radioList` mentioned above\nradioLabel                   | Assigned to the label inside of a radio button option\nradio                        | Assigned to the radio button inside of a `radioOptionsInput`\n## Default \u0026 Custom Input Types\n\nThe default set of input types that ships with Winterfell are the following:\n\n- textInput\n- textareaInput\n- emailInput\n- hiddenInput\n- fileInput\n- passwordInput\n- selectInput\n- checkboxInput\n- checkboxOptionsInput\n- radioOptionsInput\n\nYou can also define custom input types like so:\n\n```javascript\nvar Winterfell         = require('winterfell');\nvar MyAwesomeInputType = require('./awesomeInputType');\n\nWinterfell\n  .addInputType('myAwesomeInputType', MyAwesomeInputType);\n\n// OR\n\nWinterfell\n  .addInputTypes({\n    myAwesomeInputType : MyAwesomeInputType\n  });\n\n```\n\n## Custom Error Messages\n\nError messages can be set strings, or methods that are called to generate an error message. They can be set like so:\n\n```javascript\nvar Winterfell = require('winterfell');\n\nWinterfell\n  .addErrorMessage('isLength', 'Please enter some text!');\n\nWinterfell\n  .addErrorMessages({\n  \tisLength : (validationItem) =\u003e {\n  \t  /*\n  \t   * validationItem = {\n  \t   *   type   : 'isLength',\n  \t   *   params : [] //Starts with answer\n  \t   * }\n  \t   */\n\n  \t  return 'Please enter a value';\n  \t}\n  });\n```\n\n## Custom Validation Methods\n\nValidation methods can be defined and will be chosen over methods defined in the Validator package.\n\n```javascript\nvar Winterfell = require('winterfell');\n\nWinterfell\n  .addValidationMethod('isLength', value =\u003e {\n  \t/*\n  \t * arguments == validation parameters\n  \t */\n\n    return true; // Valid\n  });\n\nWinterfell\n  .addValidationMethods({\n  \tisLength : value =\u003e {\n  \t  /*\n  \t   * arguments == validation parameters\n  \t   */\n\n      return true; // valid\n    }\n  });\n```\n\n## Props \u0026 Config\n\nThe following table shows the props Winterfell accepts, their types and descriptions. The only prop that is required is `schema`.\n\nProp Name              | Type     | Description\n---                    | ---      | ---\npanelId                | string   | Initial `panelId` to render\nschema                 | object   | `schema` for the form to render\nref                    | string   | `ref` field for form element\nencType                | string   | `encType` field for the form element\nmethod                 | string   | `method` field for the form element\naction                 | string   | Default `action` field for the form element\ndisableSubmit          | boolean  | Prevent the form from submitting naturally\nquestionAnswers        | object   | Existing `questionAnswers`. `questionId` =\u003e `answer`\nrenderError            | function | Custom validation error render method. Return a React Component Or React Element.\nrenderRequiredAsterisk | function | Custom require asterisk rendering method. Return a React Component or React Element.\n\n## Events\n\nThe following events can be registered as props of Winterfell.\n\nEvent Prop | Description | Arguments\n--- | --- | ---\nonRender      | Fired when Winterfell has initially rendered   | N/A\nonUpdate      | Fired when a questions answer has been changed | `questionAnswers`\nonSwitchPanel | Fired when a panel is switched or changed      | `panel`\nonSubmit      | Fired when the form is submitted successfully  | `questionAnswers`, `action`\n\n\n\n## Final Notes\n\nPull requests are completely welcome. If you'd like to get in touch, [Tweet me](http://twitter.com/andrewhathaway). Initial schema design by [Jordan Appleson](http://twitter.com/jordanisonfire).\n\n## License\n\nMIT License (MIT)\n\nCopyright (c) 2015 Andrew Hathaway, [https://github.com/andrewhathaway/Winterfell](https://github.com/andrewhathaway/Winterfell)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewhathaway%2FWinterfell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrewhathaway%2FWinterfell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewhathaway%2FWinterfell/lists"}