├── .editorconfig ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── README.md ├── example ├── .gitignore ├── package.json ├── public │ ├── global.css │ └── index.html ├── rollup.config.js ├── src │ └── index.jsx └── yarn.lock ├── package.json ├── rollup.config.js ├── setupTest.js ├── src ├── JSONSchema │ ├── __mocks__ │ │ ├── deepFreeze.ts │ │ └── mockSchemaWithRefs.ts │ ├── __tests__ │ │ ├── formPathHandler.test.ts │ │ ├── resolveReferences.test.tsx │ │ └── testFrozenObject.test.tsx │ ├── index.ts │ ├── logic │ │ ├── index.ts │ │ ├── pathUtils.ts │ │ ├── refHandlers.ts │ │ └── schemaHandlers.ts │ ├── path-handler.ts │ └── types │ │ └── index.ts ├── __mocks__ │ └── mockObjectComponent.tsx ├── components │ ├── FormContext.tsx │ ├── __mocks__ │ │ └── mockSchema.ts │ ├── __tests__ │ │ └── FormContext.test.tsx │ ├── index.ts │ └── types │ │ └── index.ts ├── hooks │ ├── __mocks__ │ │ ├── mockSchema.ts │ │ └── mockTextSchema.ts │ ├── __tests__ │ │ ├── useCheckbox.test.tsx │ │ ├── useCustomValidator.test.tsx │ │ ├── useInput.test.tsx │ │ ├── useObject.test.tsx │ │ ├── useRadio.test.tsx │ │ ├── useRawInput.test.tsx │ │ ├── useSelect.test.tsx │ │ └── useTextArea.test.tsx │ ├── index.ts │ ├── types │ │ └── index.ts │ ├── useCheckbox.ts │ ├── useGenericInput.ts │ ├── useHidden.ts │ ├── useInput.ts │ ├── useObject.ts │ ├── usePassword.ts │ ├── useRadio.ts │ ├── useRawInput.ts │ ├── useSelect.ts │ ├── useTextArea.ts │ └── validators │ │ ├── __tests__ │ │ └── errorMessages.test.tsx │ │ ├── getEnum.ts │ │ ├── getError.ts │ │ ├── getGenericValidator.ts │ │ ├── getNumberValidator.ts │ │ ├── getStringValidator.ts │ │ ├── index.ts │ │ ├── numberUtilities.ts │ │ └── types │ │ └── index.ts └── index.ts ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | # Use 4 spaces for HTML files 13 | [*.html] 14 | indent_size = 4 15 | 16 | # The JSON files contain newlines inconsistently 17 | [*.json] 18 | insert_final_newline = ignore 19 | 20 | # Minified JavaScript files shouldn't be changed 21 | [**.min.js] 22 | indent_style = ignore 23 | insert_final_newline = ignore 24 | 25 | [*.md] 26 | trim_trailing_whitespace = false 27 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "vtex-react", 3 | "root": true, 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "es6": true, 8 | "jest": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### What problem is this solving? 2 | 3 | 4 | 5 | #### Checklist/Reminders 6 | 7 | - [ ] Updated `README.md`. 8 | - [ ] Updated `CHANGELOG.md`. 9 | - [ ] Linked this PR to a Clubhouse story (if applicable). 10 | - [ ] Updated/created tests (important for bug fixes). 11 | - [ ] Deleted the workspace after merging this PR (if applicable). 12 | 13 | #### Example usage 14 | 15 | #### Type of changes 16 | 17 | 18 | ✔️ | Type of Change 19 | ---|--- 20 | _ | Bug fix 21 | _ | New feature 22 | _ | Breaking change 23 | _ | Technical improvements 24 | 25 | #### Notes 26 | 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | *.swp 4 | *.DS_STORE 5 | output/* 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5", 5 | "eslintIntegration": true 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.2.0] - 2021-08-03 11 | 12 | ## [0.2.0-beta.13] - 2020-03-26 13 | 14 | ### Fixed 15 | 16 | - Prevent `onChange` from being triggered during the initial render. 17 | 18 | ## [0.2.0-beta.12] - 2020-03-20 19 | 20 | ### Added 21 | 22 | - `defaultValues` prop. 23 | 24 | ### Security 25 | 26 | - Upgrade all upgradable dependencies. 27 | 28 | ## [0.2.0-beta.11] - 2020-02-21 29 | 30 | ### Fixed 31 | 32 | - `getDataFromPath` to `getDataFromPointer` as declared in the `README.md`. 33 | 34 | ## [0.2.0-beta.10] - 2020-02-21 35 | 36 | ### Fixed 37 | 38 | - Typo in `ErrorTypes` 39 | 40 | ### Added 41 | 42 | - `onChange` and `formProps` props to `FormContext`. 43 | 44 | ### Changed 45 | 46 | - **BREAKING**: no longer uses paths starting with `$` (which was added at version `0.2.0-beta.6`) and all 'paths' should now be relative to the JSON Schema, respecting the [RFC 6901](https://tools.ietf.org/html/rfc6901) which defines the string syntax for JSON Pointers, pay special attention to [section 6](https://tools.ietf.org/html/rfc6901#section-6) which defines the URI frament identifier representation of JSON pointers. 47 | 48 | ## [0.2.0-beta.9] - 2020-02-18 49 | 50 | ### Fixed 51 | 52 | - `notInEnum` to return expected value in `expected` field of the `ErrorMessage`. 53 | 54 | ## [0.2.0-beta.8] - 2020-02-17 55 | 56 | ### Changed 57 | 58 | - **BREAKING**: `number` error messages to now return messages describing whether the input does not match the expected pattern for a number. 59 | 60 | ## [0.2.0-beta.7] - 2020-02-14 61 | 62 | ### Changed 63 | 64 | - **BREAKING**: `onSubmit` now passes an object with `data`, `event` and `methods` as members to the callback. 65 | 66 | ## [0.2.0-beta.6] - 2020-02-13 67 | 68 | ### Added 69 | 70 | - `$ref` and `$id` resolving in accord to the [JSON Schema specification](https://tools.ietf.org/html/draft-wright-json-schema-01) 71 | 72 | ### Changed 73 | 74 | - **BREAKING**: Now uses paths starting with `$` to represent objects of an instance of the JSON Schema, instead of a path starting with `#`, which resembled a URI fragment identifier representation of a JSON pointer. 75 | - custom validator `context` parameter now gives info as an annotated sub schema 76 | 77 | ### Fixed 78 | 79 | - Not checking if value exists before using enum validation on it 80 | - `isRequired` not evaluating correctly if it is inside another object that is not required 81 | - Empty data not being ignored when parsing form data 82 | 83 | ## [0.2.0-beta.5] - 2020-02-11 84 | 85 | ### Added 86 | 87 | - `customValidators` to allow the user to define their own validation functions 88 | 89 | ### Changed 90 | 91 | - **BREAKING**: Renamed `FormValuesWithSchema` type to `JSONFormContextValues` 92 | 93 | ## [0.2.0-beta.4] - 2020-02-05 94 | 95 | ### Fixed 96 | 97 | - Returning non-filled form inputs in the object passed to onSubmit 98 | 99 | ## [0.2.0-beta.3] - 2020-01-31 100 | 101 | ### Changed 102 | 103 | - **BREAKING**: Changed `InputTypes` and `UITypes` enum values to reflect their enum names and what was documented in the `README` 104 | - **BREAKING**: Changed `min` and `max` props returned from `getInputProps()` to be strings, not numbers 105 | - **BREAKING**: Just re-exports types and `Controller` component from `react-hook-form` 106 | 107 | ## [0.2.0-beta.2] - 2020-01-30 108 | 109 | ### Fixed 110 | 111 | - type of onChange possibly being undefined 112 | 113 | ## [0.2.0-beta.1] - 2020-01-29 114 | 115 | ### Added 116 | 117 | - Added `useCheckbox` hook 118 | 119 | ### Changed 120 | 121 | - Changed the BasicInputReturnType to also return a reference to the validator used 122 | - **BREAKING**: The `useObject` hook now automatically associates a boolean to a checkbox 123 | 124 | ### Fixed 125 | 126 | - Fixed returned form data not converting string values that represent booleans to actual booleans 127 | 128 | ## [0.2.0-beta.0] - 2020-01-23 129 | 130 | ### Changed 131 | 132 | - Renamed `'mode'` prop on FormContext to `'validationMode'` 133 | 134 | ## [0.2.0-beta] - 2020-01-22 135 | 136 | ### Added 137 | 138 | - Now re-exports the `react-hook-form` public API 139 | 140 | ## [0.1.3] - 2020-01-21 141 | 142 | ### Added 143 | 144 | - Added test to make sure schema is not modified 145 | 146 | ### Changed 147 | 148 | - Fixed tons of typos on README 149 | - Made README more friendly 150 | - Refactored internal API to be more easily expandable 151 | - Removed complexity from big function bodies 152 | 153 | ## [0.1.2] - 2020-01-21 154 | 155 | ### Added 156 | 157 | - Typings for JSON Schema object 158 | - Removed unused mock 159 | 160 | ### Changed 161 | 162 | - Add typings for JSON Schema object 163 | - Build process is cleaner 164 | - React hook form is now an external dependency, not bundled with the code anymore 165 | 166 | ## [0.1.1] - 2020-01-17 167 | 168 | ### Added 169 | 170 | - Initial implementation. 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-hook-form-jsonschema 2 | 3 | > Small project based on [react-hook-form](https://github.com/react-hook-form/react-hook-form) that exposes an API for easily creating customizable forms based on a [JSON Schema](https://json-schema.org/understanding-json-schema/index.html) with built-in validation. 4 | 5 | `react-hook-form-jsonschema` is a React hooks library that manages all the stateful logic needed to make a functional form based on a JSON Schema. It returns a set of props that are meant to be called and their results destructured on the desired input field. 6 | 7 | Try a live demo on [CodeSandbox](https://codesandbox.io/s/react-hook-form-jsonschema-basic-example-u68o7)! 8 | 9 | [Supported JSON Schema keywords](#supported-json-schema-keywords) 10 | 11 | ## Table of Contents 12 | 13 | - [react-hook-form-jsonschema](#react-hook-form-jsonschema) 14 | - [Table of Contents](#table-of-contents) 15 | - [Simple Usage](#simple-usage) 16 | - [Installation](#installation) 17 | - [API](#api) 18 | - [Components API](#components-api) 19 | - [FormContext component API](#formcontext-component-api) 20 | - [Functions API](#functions-api) 21 | - [getDataFromPointer(pointer, data)](#getdatafrompointerpointer-data) 22 | - [Hooks API](#hooks-api) 23 | - [useCheckbox(pointer)](#usecheckboxpointer) 24 | - [useHidden(pointer)](#usehiddenpointer) 25 | - [useInput(pointer)](#useinputpointer) 26 | - [useObject(pointer, UISchema)](#useobjectpointer-uischema) 27 | - [usePassword(pointer)](#usepasswordpointer) 28 | - [useRadio(pointer)](#useradiopointer) 29 | - [useSelect(pointer)](#useselectpointer) 30 | - [useTextArea(pointer)](#usetextareapointer) 31 | - [Supported JSON Schema keywords](#supported-json-schema-keywords) 32 | - [TODO/Next Steps](#todonext-steps) 33 | - [Useful resources](#useful-resources) 34 | 35 | ## Simple Usage 36 | 37 | Suppose you have a simple JSON Schema that stores a person's first name: 38 | 39 | ```js 40 | const personSchema = { 41 | $id: 'https://example.com/person.schema.json', 42 | $schema: 'http://json-schema.org/draft-07/schema#', 43 | title: 'Person', 44 | type: 'object', 45 | properties: { 46 | firstName: { 47 | type: 'string', 48 | description: "The person's first name.", 49 | }, 50 | }, 51 | } 52 | ``` 53 | 54 | And suppose you want to create a form field for the `firstName` field, simply use the `useInput()` hook and render the form using react: 55 | 56 | ```JSX 57 | function FirstNameField(props) { 58 | const inputMethods = useInput('#/properties/firstName'); 59 | 60 | return ( 61 | 62 | 65 | 66 | 67 | ) 68 | } 69 | ``` 70 | 71 | ## Installation 72 | 73 | With npm: 74 | 75 | ``` 76 | npm install react-hook-form-jsonschema --save 77 | ``` 78 | 79 | Or with yarn: 80 | 81 | ``` 82 | yarn add react-hook-form-jsonschema 83 | ``` 84 | 85 | ## API 86 | 87 | This is the API documentation, `react-hook-form-jsonschema` also re-exports all the [`react-hook-form`](https://github.com/react-hook-form/react-hook-form) types and the `Controller` component. All of the other functionalities are abstracted by this library. 88 | 89 | ## Components API 90 | 91 | ### FormContext component API 92 | 93 | This component is the top-level component that creates the context with the schema and options all the hooks will need to be usable. So bear in mind that you **need** to define all the other components as children of `FormContext`. 94 | 95 | #### props: 96 | 97 | ##### Required: 98 | 99 | - `schema`: JSON Schema object which will be passed down by context for the inputs to use it for validation and the structure of the form itself. 100 | 101 | ##### Optional: 102 | 103 | - `customValidators`: An object where each member has to be a funtion with the following format: 104 | - `function(value: string, context: SubSchemaInfo) => CustomValidatorReturnValue` 105 | - `params`: 106 | - `value`: Is the current value in the form input. 107 | - `context`: Is an object with the following fields: 108 | - `JSONSchema`: Is the sub schema of the current field 109 | - `isRequired`: Whether the current field is required or not 110 | - `objectName`: The name of the sub schema 111 | - `invalidPointer`: A `boolean` indicating whether the referenced field was found within the schema or not. If it is false it is because of an error in the schema. 112 | - `pointer`: JSON Pointer to sub schema that should be validated. The pointer is always in the form: `#/properties/some/properties/data` where `#` represents the root of the schema, and the `properties/some/properties/data` represents the tree of objects (from `some` to `data`) to get to the desired field, which in this case is `data`. Also see the definition of JSON Pointers on [RFC 6901](https://tools.ietf.org/html/rfc6901). 113 | - `return value`: Must be either a `string` that identifies the error or a `true` value indicating the validation was succesfull. 114 | - `formProps`: An object that is passed to the underlying `
` element. Accepts the same attributes as when declaring a `` with React, except `onSubmit`. 115 | - `validationMode`: String to indicate when to validate the input, default is `'onSubmit'`. 116 | - `'onBlur'`: Validate when an input field is blurred. 117 | - `'onChange'`: Validate when an input field value changes. 118 | - `'onSubmit'`: Validate when the submit is triggered. 119 | - `revalidateMode`: String to indicate when inputs with errors get re-validated, default is `'onChange'`. 120 | - `'onblur'`: Validate when an input field is blurred. 121 | - `'onChange'`: Validate when an input field value changes. 122 | - `'onSubmit'`: Validate when the submit is triggered. 123 | - `submitFocusError`: Boolean, when `true` focus on the first field with error after submit validates, if there is any. Defaults to `true`. 124 | - `onChange`: Callback called when there's a change in the form. It passes the form _data_ formatted by the provided JSON Schema. 125 | - `onSubmit`: If provided `react-hook-form-jsonschema` will call this function as the submit action, it passes an object with the following members: 126 | - `data`: The data that was provided as inputs to the form, correctly formatted as an instance of the JSON Schema provided. 127 | - `event`: A react event 128 | - `methods`: Provides access to the methods of [`react-hook-form`](https://react-hook-form.com/api) `useForm`, from this you can extract, for example, the `triggerValidation` method to revalidate the form if an error occured while submitting. 129 | - `noNativeValidate`: Boolean, when `true` disables the default browser validation (notice that `react-hook-form-jsonschema` does NOT yet implement validation for URIs and email addresses). 130 | - `defaultValues`: An object that defines the form's default values. 131 | 132 | ## Functions API 133 | 134 | ### getDataFromPointer(pointer, data) 135 | 136 | **Description** 137 | 138 | Gets a specific member of data given a pointer. 139 | 140 | **Parameters** 141 | 142 | - `pointer`: JSON Pointer to the desired sub schema that will be rendered. 143 | - `data`: An object, as the one passed as parameter to the `onSubmit` function. 144 | 145 | **Return** 146 | 147 | Returns the data indicated by the pointer inside an instance of a JSON Schema the object. Or undefined if the pointer is not found. 148 | 149 | **Example** 150 | 151 | ```JSX 152 | // Suppose you have a schema with the following format: 153 | const schema = { 154 | type: 'object', 155 | properties: { 156 | address: { 157 | type: 'object', 158 | properties: { 159 | name: { type: 'string' } 160 | } 161 | } 162 | } 163 | } 164 | 165 | // A valid instance of this schema is this: 166 | const data = { 167 | address: { 168 | name: "Foo" 169 | } 170 | } 171 | 172 | // And suppose you have a pointer to a sub schema only: 173 | const pointer = '#/properties/address/properties/name' 174 | 175 | // Use this function to get the data from the instance of the schema(data) that 176 | // coincides with the sub schema the pointer points to. 177 | const result = getDataFromPointer(pointer, data) // returns "Foo" 178 | ``` 179 | 180 | ## Hooks API 181 | 182 | The following are the common fields returned in the object from every `use'SomeInputType'` hook: 183 | 184 | - `type`: The type of the input, as defined in **`InputTypes`**: 185 | - `generic`: the default type, a non specialized type, only contains the common fields 186 | - `radio`: Type used for `` 187 | - `select`: Type used for `` 189 | - `textArea`: Type used for `