From 5cd65ca2c47d2ee3573d0178d755b6448cd8e6fc Mon Sep 17 00:00:00 2001 From: Volodymyr Zakhovaiko Date: Fri, 17 Jan 2025 13:24:02 +0100 Subject: [PATCH 1/4] Added API reference docs --- website/docs/api-reference/_category_.json | 5 + website/docs/api-reference/bridges.mdx | 288 ++++++++ website/docs/api-reference/context-data.mdx | 132 ++++ website/docs/api-reference/fields.mdx | 687 ++++++++++++++++++++ website/docs/api-reference/forms.mdx | 462 +++++++++++++ website/docs/api-reference/helpers.mdx | 217 +++++++ 6 files changed, 1791 insertions(+) create mode 100644 website/docs/api-reference/_category_.json create mode 100644 website/docs/api-reference/bridges.mdx create mode 100644 website/docs/api-reference/context-data.mdx create mode 100644 website/docs/api-reference/fields.mdx create mode 100644 website/docs/api-reference/forms.mdx create mode 100644 website/docs/api-reference/helpers.mdx diff --git a/website/docs/api-reference/_category_.json b/website/docs/api-reference/_category_.json new file mode 100644 index 000000000..12c1a64c1 --- /dev/null +++ b/website/docs/api-reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "label": "API Reference", + "collapsible": false +} diff --git a/website/docs/api-reference/bridges.mdx b/website/docs/api-reference/bridges.mdx new file mode 100644 index 000000000..b76ef3a28 --- /dev/null +++ b/website/docs/api-reference/bridges.mdx @@ -0,0 +1,288 @@ +--- +id: bridges +title: 'Bridges' +sidebar_position: 3 +--- + +To make use of any schema, uniforms have to create a _bridge_ of it - a unified schema mapper. + +

+ +

+ +Currently available bridges: + +- `JSONSchemaBridge` in `uniforms-bridge-json-schema` ([schema documentation](https://json-schema.org/)) +- `SimpleSchema2Bridge` in `uniforms-bridge-simple-schema-2` ([schema documentation](https://github.com/longshotlabs/simpl-schema#readme)) +- `ZodBridge` in `uniforms-bridge-zod` ([schema documentation](https://zod.dev/)) + +Deprecated bridges: + +- `SimpleSchemaBridge` in `uniforms-bridge-simple-schema` ([schema documentation](https://github.com/Meteor-Community-Packages/meteor-simple-schema/blob/master/DOCS.md)) +- `GraphQLBridge` in `uniforms-bridge-graphql` ([schema documentation](https://graphql.org/)) + +If you see a lot of [`Warning: Unknown props...`](https://fb.me/react-unknown-prop) logs, check if your schema or theme doesn't provide extra props. If so, consider [registering it with `filterDOMProps`](/docs/api-helpers#filterdomprops). + +## `JSONSchemaBridge` + +```tsx +import Ajv from 'ajv'; +import { JSONSchemaBridge } from 'uniforms-bridge-json-schema'; + +const ajv = new Ajv({ allErrors: true, useDefaults: true }); + +const schema = { + title: 'Person', + type: 'object', + properties: { + firstName: { type: 'string' }, + lastName: { type: 'string' }, + age: { + description: 'Age in years', + type: 'integer', + minimum: 0, + }, + }, + required: ['firstName', 'lastName'], +}; + +function createValidator(schema: object) { + const validator = ajv.compile(schema); + + return (model: object) => { + validator(model); + return validator.errors?.length ? { details: validator.errors } : null; + }; +} + +const validator = createValidator(schema); + +const bridge = new JSONSchemaBridge({ schema, validator }); +``` + +### Note on `allOf`/`anyOf`/`oneOf` + +The current handling of `allOf`/`anyOf`/`oneOf` is not complete and does not work with all possible cases. For an in-detail discussion, see [\#863](https://github.com/vazco/uniforms/issues/863). How it works, is that only a few properties are being used: + +- `properties`, where all subfields are merged (last definition wins), +- `required`, where all properties are accumulated, and +- `type`, where the first one is being used. + +Below is an example of these implications: + +```ts +{ + "type": "object", + "properties": { + // This will render `NumField` WITHOUT `min` nor `max` properties. + // It will be properly validated, but without any UI guidelines. + "foo": { + "type": "number", + "allOf": [{ "minimum": 0 }, { "maximum": 10 }] + }, + // This will render as `TextField`. + "bar": { + "oneOf": [{ "type": "string" }, { "type": "number" }] + } + } +} +``` + +### Note on Bluebird + +If you're using the [`bluebird`](https://www.npmjs.com/package/bluebird) package, you may have seen the following warning ([docs](http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-rejected-with-a-non-error)): + +> Warning: a promise was rejected with a non-error [object Object] + +There could be multiple causes of this error. One of it is not returning a proper error object. + +In order to fix it, your `validator` function should return a `Error`-like object instead of an object with a single `details` property. The cleanest would be to create a custom `ValidationError` class: + +```ts +import { ErrorObject } from 'ajv'; + +class ValidationError extends Error { + name = 'ValidationError'; + + constructor(public details: ErrorObject[]) { + super('ValidationError'); + } +} + +// Usage. +return validator.errors?.length ? new ValidationError(validator.errors) : null; +``` + +Another cause of this error may be two different implementations of the `Promise` object when using an asynchronous validate function. +Ensure that you are returning the same `Promise` object implementation that Bluebird is expecting. +The simplest way to do that should be to avoid using the `async` keyword and instead make the function return a `Promise` instead. + +See [#1047](https://github.com/vazco/uniforms/discussions/1047) for more details. + +## `SimpleSchema2Bridge` + +```tsx +import SimpleSchema from 'simpl-schema'; +import SimpleSchema2Bridge from 'uniforms-bridge-simple-schema-2'; + +const schema = new SimpleSchema({ + // ... + + aboutMe: { + type: String, + uniforms: MyText, // Component... + uniforms: { + // ...or object... + component: MyText, // ...with component... + propA: 1, // ...and/or extra props. + }, + }, +}); + +const bridge = new SimpleSchema2Bridge({ schema }); +``` + +## `ZodBridge` + +```tsx +import ZodBridge from 'uniforms-bridge-zod'; +import z from 'zod'; + +const schema = z.object({ aboutMe: z.string() }); + +const bridge = new ZodBridge({ schema }); +``` + +## `SimpleSchemaBridge` + +:::caution + +SimpleSchemaBridge is deprecated since uniforms v4. + +::: + +```tsx +import SimpleSchemaBridge from 'uniforms-bridge-simple-schema'; +import { SimpleSchema } from 'aldeed:simple-schema'; + +const schema = new SimpleSchema({ + // ... + + aboutMe: { + type: String, + uniforms: MyText, // Component... + uniforms: { + // ...or object... + component: MyText, // ...with component... + propA: 1, // ...and/or extra props. + }, + }, +}); + +const bridge = new SimpleSchemaBridge({ schema }); +``` + +## `ZodBridge` + +```tsx +import ZodBridge from 'uniforms-bridge-zod'; +import z from 'zod'; + +const schema = z.object({ aboutMe: z.string() }); + +const bridge = new ZodBridge({ schema }); +``` + +## `GraphQLBridge` + +:::caution + +GraphQLBridge is deprecated since uniforms v4. + +::: + +This bridge enables using GraphQL schema types as uniforms forms. +This saves you from not having to rewrite the form schema in your code. +As a trade-off, you have to write the validator from scratch. In some cases, it might be easier to rewrite the schema and use, for example, [`JSONSchemaBridge` with `ajv`](/docs/api-bridges#jsonschemabridge). +If only a simple or no validation is needed, this bridge is perfectly suited to work with GraphQL schemas. + +The constructor accepts these arguments: + +- `schema: GraphQLType` can be any type parsed and extracted from a GraphQL schema. +- `validator: (model: Record) => any` a custom validator function that should return a falsy value if no errors are present or information about errors in the model as described in the [custom bridge section](/docs/examples-custom-bridge#validator-definition). +- `extras: Record = {}` used to extend the schema generated from GraphQL type with extra field configuration. +- `provideDefaultLabelFromFieldName = true` if set to `true`, the bridge will use the field name as a label if no label is provided in the schema. + +### Code example + +```tsx +import { GraphQLBridge } from 'uniforms-bridge-graphql'; +import { buildASTSchema, parse } from 'graphql'; + +const schema = ` + type Author { + id: String! + firstName: String + lastName: String + } + + type Post { + id: Int! + author: Author! + title: String + votes: Int + } + + # This is required by buildASTSchema + type Query { anything: ID } +`; + +const schemaType = buildASTSchema(parse(schema)).getType('Post'); +const schemaExtras = { + id: { + options: [ + { label: 1, value: 1 }, + { label: 2, value: 2 }, + { label: 3, value: 3 }, + ], + }, + title: { + options: [ + { label: 1, value: 'a' }, + { label: 2, value: 'b' }, + ], + }, + 'author.firstName': { + placeholder: 'John', + }, +}; + +const schemaValidator = (model: object) => { + const details = []; + + if (!model.id) { + details.push({ name: 'id', message: 'ID is required!' }); + } + + if (!model.author.id) { + details.push({ name: 'author.id', message: 'Author ID is required!' }); + } + + if (model.votes < 0) { + details.push({ + name: 'votes', + message: 'Votes must be a non-negative number!', + }); + } + + // ... + + return details.length ? { details } : null; +}; + +const bridge = new GraphQLBridge({ + schema: schemaType, + validator: schemaValidator, + extras: schemaExtras, +}); +``` diff --git a/website/docs/api-reference/context-data.mdx b/website/docs/api-reference/context-data.mdx new file mode 100644 index 000000000..6fcfd7029 --- /dev/null +++ b/website/docs/api-reference/context-data.mdx @@ -0,0 +1,132 @@ +--- +id: context-data +title: 'Context data' +sidebar_position: 4 +--- + +Some components might need to know a current form state, which is passed as [React context](https://reactjs.org/docs/context.html). +Properties stored in the context relates either to the form values or the form instance itself. +That means, besides current form state, you can access form methods or encounter some metadata, e.g. about the used schema. +Some of them were designed for internal use, but you can still take advantage of them. + +## Accessing context data + +A direct way of accessing the context data is to use the [`useForm`](/docs/api-helpers/#useform) hook: + +```tsx +import { useForm } from 'uniforms'; + +function Example() { + const context = useForm(); +} +``` + +For convenience, it's also accessible through the [`useField`](/docs/api-helpers/#usefield) hook: + +```tsx +import { useField } from 'uniforms'; + +function Example(rawProps) { + const [props, context] = useField(rawProps.name, rawProps); +} +``` + +## Available context data + +### `changed` + +Indicates whether there was a change on form. + +### `changedMap` + +A map of changed fields. Rather internal one, used for checking if _other_ fields has changed. + +### `error` + +An object with a `details` field, which is an array of any validation errors. + +### `formRef` + +Contains reference to the form component that gives access to [the form methods](/docs/api-forms#methods). + +### `model` + +An object with current form fields values structured `{field: value}`. + +### `name` + +It is an array of the parent fields names: + +```tsx + + // name = [] + + // name = ['x'] + // name = ['x', 'y', 'z'] + + +``` + +For example if we define a `CompositeField`: + +```tsx +const Composite = () => ( +
+ + +
+); +``` + +And use it like that: + +```tsx + + + + +``` + +The `name` array of the nested `AutoFields` will store a `personA` value. + +### `onChange` + +You can directly access to the `onChange` method. E.g. `onChange(field, value)`. + +### `onSubmit` + +Access to `onSubmit` method. + +### `randomId` + +Access to `randomId` method, created using the [`randomIds()`](/docs/api-helpers#randomids) helper. + +### `schema` + +A bridge class instance with `schema` and `validator` properties. + +The `schema` is simply your schema object and `validator` is your validating function. + +### `state` + +The `state` is an object representing your current form status. + +The state properties are: + +| Name | Description | +| :---------------: | :--------------------------------------------------: | +| `disabled` | Indicates whether the form is disabled. | +| `readOnly` | Indicates whether the form is read-only. | +| `showInlineError` | Indicates whether the inline errors should be shown. | + +### `submitted` + +Indicates whether the form was submitted. + +### `submitting` + +Indicates whether the form is in the `submitting` state. Helpful when handling asynchronous submission. + +### `validating` + +Indicates whether the form is in the `validating` state. Helpful when handling asynchronous validation. diff --git a/website/docs/api-reference/fields.mdx b/website/docs/api-reference/fields.mdx new file mode 100644 index 000000000..0a62c0155 --- /dev/null +++ b/website/docs/api-reference/fields.mdx @@ -0,0 +1,687 @@ +--- +id: fields +title: 'Fields' +sidebar_position: 2 +--- + +uniforms provide a set of predefined components that can be used as form fields. + +The list below contains a guaranteed set of fields, implemented in every theme package: + +| Component | Description | +| :-------------: | :-----------------------------------------------: | +| `AutoField` | Automatically renders a given field. | +| `AutoFields` | Automatically renders given fields. | +| `BoolField` | Checkbox. | +| `DateField` | HTML5 `date` or `datetime-local` input. | +| `ErrorField` | Error message for a given field. | +| `ErrorsField` | Error message with a list of validation errors. | +| `HiddenField` | Hidden field (with a possibility to omit in DOM). | +| `ListAddField` | An icon with action to add a list item. | +| `ListDelField` | An icon with action to remove a list item. | +| `ListField` | List of nested fields. | +| `ListItemField` | Single list item wrapper. | +| `LongTextField` | Textarea. | +| `NestField` | Block of nested fields. | +| `NumField` | Numeric input. | +| `RadioField` | Radio checkbox. | +| `SelectField` | Select (or set of radio checkboxes). | +| `SubmitField` | Submit button. | +| `TextField` | Text (or any HTML5 compatible) input. | + +## Fields + +### `AutoField` + +`AutoField` is basically a field renderer - it will render a field of a type adequate to the one defined in the schema, +according to the [`AutoField` algorithm](/docs/uth-autofield-algorithm). +You can also directly pass a component to it (by a `component` prop). +All additional props will be passed to the result field component. + +##### Props: + +| Name | Default | Description | +| :---------: | :------------------------------------------: | :-----------------------------: | +| `component` | Field according to the `AutoField` algorithm | Component to render | +| `name` | - | Name of the field in the schema | + +##### Props usage: + +```tsx +import { AutoField } from 'uniforms-unstyled'; + +; +``` + +### `AutoFields` + +`AutoFields` is basically a set of rendered `AutoField`s. +By default, the rendered fields will be `AutoField` in a chosen theme. +However, you can replace the standard `AutoField` with [`AutoField.componentDetectorContext`](/docs/uth-autofield-algorithm#overriding-autofield). + +The `element` property defines a wrapping component. +E.g. you want to group your fields inside a section, just do `element="section"`. The default one is `div`. + +##### Props: + +| Name | Default | Description | +| :----------: | :---------------: | :----------------------: | +| `element` | `div` | Fields wrapper | +| `fields` | All schema fields | List of fields to render | +| `omitFields` | `[]` | List of fields to omit | + +##### Props usage: + +```tsx +import { AutoFields } from 'uniforms-unstyled'; + +; +``` + +### `BoolField` + +A checkbox. + +##### Props: + +| Name | Description | Available in | +| :---------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `appearance` | Field appearance. Set to "toggle" to appear as a Material Toggle or to "checkbox" (or leave it undefined) to use a Checkbox appearance. | material, mui | +| `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | +| `grid` | Field layout. Bootstrap grid layout style. Number is an equivalent of n. Object is a size object. Complete string is simply passed through. | bootstrap4, bootstrap5 | +| `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | +| `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `inline` | Checkbox inline state. In bootstrap themes, a label is rendered as a text but in inline mode, it's treated as a field label. | bootstrap4, bootstrap5 | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `inputRef` | Setting ref prop to a field won't work as desired, because you'll receive a field component rather than an HTML input. If you need an input ref, use this prop instead. | All | +| `labelBefore` | Left label. In bootstrap themes, label is rendered on the right side of a checkbox. This label is rendered above the field. | bootstrap4, bootstrap5 | +| `labelClassName` | Label className. A custom className for the field's label. | bootstrap4, bootstrap5 | +| `labelCol` | Field layout. The layout of label. You can set span and/or offset. | antd | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | +| `wrapClassName` | Field and surroundings wrap className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `wrapperCol` | Field layout. The layout for input controls. Same as labelCol. | antd | + +##### Props usage: + +```tsx +import { BoolField } from 'uniforms-unstyled'; +import { useRef } from 'react' + +const inputRef = useRef(); + +; +``` + +### `DateField` + +##### Props: + +| Name | Description | Available in | +| :---------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | +| `grid` | Field layout. Bootstrap grid layout style. Number is an equivalent of n. Object is a size object. Complete string is simply passed through. | bootstrap4, bootstrap5 | +| `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | +| `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `icon` | Input icon. Semantic inputs can have an icon. By default, it's placed on the right side - to place it on the left, use `iconLeft` prop instead. | semantic | +| `iconLeft` | Semantic inputs can have an icon. With this prop, it's placed on the left side - to place it on the right, use icon prop instead. | semantic | +| `iconProps` | Input icon props. Semantic inputs can have an icon. These props are passed directly to the icon element. | semantic | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `inputRef` | Setting ref prop to a field won't work as desired, because you'll receive a field component rather than an HTML input. If you need an input ref, use this prop instead. | All | +| `labelClassName` | Label className. A custom className for the field's label. | bootstrap4, bootstrap5 | +| `labelCol` | Field layout. The layout of label. You can set span and/or offset. | antd | +| `labelProps` | Props for the InputLabel | material, mui | +| `max` | Maximum value. Date object. | All | +| `min` | Minimal value. Date object. | All | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | +| `timeFormat` | Display time picker in ampm (12hr) format or 24hr format. | material, mui | +| `type` | Display date and time or date only picker. Format value according to its type | All except antd | +| `wrapClassName` | Field and surroundings wrap className. In SemanticUI theme, this class name is used on ui input wrapper, so you can pass classes like small, huge, inverted, transparent etc. | bootstrap4, bootstrap5, semantic | +| `wrapperCol` | Field layout. The layout for input controls. Same as labelCol. | antd | + +##### Props usage: + +```tsx +import { DateField } from 'uniforms-unstyled'; +import { useRef } from 'react' + +const inputRef = useRef(); + +; +``` + +### `ErrorField` + +Error message renderer. + +##### Props: + +| Name | Description | +| :--------: | :--------------------------------------------------------------------------------------------------------------------: | +| `children` | Custom content. By default, it will render a block with the error message (if any), but you can customize the content. | +| `name` | Target field. This field error should be used. | + +##### Props usage: + +```tsx +import { ErrorField } from 'uniforms-unstyled'; + +; +``` + +### `ErrorsField` + +Error messages renderer. + +##### Props: + +| Name | Description | +| :--------: | :---------------------------------------------------------------------------------------------------------------------: | +| `children` | Custom content. By default, it will render a block with the error messages (if any), but you can customize the content. | + +##### Props usage: + +```tsx +import { ErrorsField } from 'uniforms-unstyled'; + +; +``` + +### `HiddenField` + +##### Props: + +| Name | Description | +| :-----: | :-------------------------------------------------------------------------------------------------------------------------------------------------------: | +| `name` | Field name. Used for identification. It should match your schema - if not, it will throw an error. | +| `value` | Field value. This field has completely different semantics. When a value is set, then it's updating a current model instead of being passed to the field. | + +##### Props usage: + +```tsx +import { HiddenField } from 'uniforms-unstyled'; + +; +``` + +### `ListAddField` + +##### Props: + +| Name | Description | Available in | +| :-------: | :----------------------------------: | :--------------------------------: | +| `addIcon` | Icon. By default, glyphicon is used. | bootstrap4, bootstrap5 | + +**Note:** This is one of _internal_ components of `ListField`. + +##### Props usage: + +```tsx +import { ListAddField } from 'uniforms-unstyled'; + +} />; +``` + +### `ListDelField` + +##### Props: + +| Name | Description | Available in | +| :----------: | :----------------------------------: | :--------------------------------: | +| `removeIcon` | Icon. By default, glyphicon is used. | bootstrap4, bootstrap5 | + +**Note:** This is one of _internal_ components of `ListField`. + +##### Props usage: + +```tsx +import { ListDelField } from 'uniforms-unstyled'; + +} />; +``` + +### `ListField` + +##### Props: + +| Name | Default | Description | Available in | +| :---------------: | :-------: | :------------------------------------------------------------------------------------: | :------------------------------------------: | +| `addIcon` | glyphicon | Icon. It's passed to the ListAddField. | bootstrap4, bootstrap5 | +| `initialCount` | - | Initial items count. At least this amount of fields will be rendered at the beginning. | All | +| `itemProps` | - | ListItemField props. These props are passed to the ListItemField. | All | +| `removeIcon` | glyphicon | Icon. It's passed to the ListDelField. | bootstrap4, bootstrap5 | +| `showInlineError` | | Field inline error. _Some description would be great, huh?_ | bootstrap4, bootstrap5, semantic | + +##### Props usage: + +```tsx +import { ListField } from 'uniforms-unstyled'; + +} + initialCount={5} + itemProps={ + { + /* ... */ + } + } + removeIcon={} + showInlineError +/>; +``` + +### `ListItemField` + +| Name | Default | Description | Available in | +| :----------: | :-------: | :------------------------------------: | :--------------------------------: | +| `removeIcon` | glyphicon | Icon. It's passed to the ListDelField. | bootstrap4, bootstrap5 | + +**Note:** This is one of _internal_ components of `ListField`. + +##### Props usage: + +```tsx +import { ListItemField } from 'uniforms-unstyled'; + +} />; +``` + +### `LongTextField` + +A textarea. + +##### Props: + +| Name | Description | Available in | +| :---------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | +| `grid` | Field layout. Bootstrap grid layout style. Number is an equivalent of n. Object is a size object. Complete string is simply passed through. | bootstrap4, bootstrap5 | +| `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | +| `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `icon` | Input icon. Semantic inputs can have an icon. By default, it's placed on the right side - to place it on the left, use `iconLeft` prop instead. | semantic | +| `iconLeft` | Semantic inputs can have an icon. With this prop, it's placed on the left side - to place it on the right, use icon prop instead. | semantic | +| `iconProps` | Input icon props. Semantic inputs can have an icon. These props are passed directly to the icon element. | semantic | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `inline` | Checkbox inline state. In bootstrap themes, a label is rendered as a text but in inline mode, it's treated as a field label. | bootstrap4 | +| `inputRef` | Setting ref prop to a field won't work as desired, because you'll receive a field component rather than an HTML input. If you need an input ref, use this prop instead. | All | +| `labelBefore` | Left label. In bootstrap themes, label is rendered on the right side of a checkbox. This label is rendered above the field. | bootstrap4, bootstrap5 | +| `labelClassName` | Label className. A custom className for the field's label. | bootstrap4, bootstrap5 | +| `labelCol` | Field layout. The layout of label. You can set span and/or offset. | antd | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | +| `wrapperCol` | Field layout. The layout for input controls. Same as labelCol. | antd | +| `wrapClassName` | Field and surroundings wrap className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | + +##### Props usage: + +```tsx +import { LongTextField } from 'uniforms-unstyled'; +import { useRef } from 'react' + +const inputRef = useRef(); + +; +``` + +### `NestField` + +##### Props: + +| Name | Description | Available in | +| :---------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `fields` | Array of rendered fields. If no custom content provided, only those fields are rendered. By default, All of nested fields are rendered. | All | +| `grouped` | Add / remove "grouped" class from the field. In Semantic, fields can be grouped using this class. By default, this class is added to the NestField. | semantic | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | + +##### Props usage: + +```tsx +import { NestField } from 'uniforms-unstyled'; + +; +``` + +### `NumField` + +A numeric input field. + +##### Props: + +| Name | Description | Available in | +| :---------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `decimal` | Decimal mode. This will change value step from 1 to 0.01. | All | +| `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | +| `grid` | Field layout. Bootstrap grid layout style. Number is an equivalent of n. Object is a size object. Complete string is simply passed through. | bootstrap4, bootstrap5 | +| `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | +| `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `icon` | Input icon. Semantic inputs can have an icon. By default, it's placed on the right side - to place it on the left, use `iconLeft` prop instead. | semantic | +| `iconLeft` | Semantic inputs can have an icon. With this prop, it's placed on the left side - to place it on the right, use icon prop instead. | semantic | +| `iconProps` | Input icon props. Semantic inputs can have an icon. These props are passed directly to the icon element. | semantic | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `inline` | Checkbox inline state. In bootstrap themes, a label is rendered as a text but in inline mode, it's treated as a field label. | bootstrap4 | +| `inputRef` | Setting ref prop to a field won't work as desired, because you'll receive a field component rather than an HTML input. If you need an input ref, use this prop instead. | All | +| `labelBefore` | Left label. In bootstrap themes, label is rendered on the right side of a checkbox. This label is rendered above the field. | bootstrap4 | +| `labelClassName` | Label className. A custom className for the field's label. | bootstrap4, bootstrap5 | +| `labelCol` | Field layout. The layout of label. You can set span and/or offset. | antd | +| `max` | Maximum value. Date object. | All | +| `min` | Minimal value. Date object. | All | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | +| `step` | Input step. | All | +| `wrapperCol` | Field layout. The layout for input controls. Same as labelCol. | antd | +| `wrapClassName` | Field and surroundings wrap className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | + +##### Props usage: + +```tsx +import { NumField } from 'uniforms-unstyled'; +import { useRef } from 'react' + +const inputRef = useRef(); + +; +``` + +### `RadioField` + +##### Props: + +| Name | Description | Available in | +| :---------------: | :---------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `inline` | Checkbox inline state. In bootstrap themes, a label is rendered as a text but in inline mode, it's treated as a field label. | bootstrap4, bootstrap5 | +| `options` | Options. It can be either an object or an array (or a function, that returns it). | All | +| `labelCol` | Field layout. The layout of label. You can set span and/or offset. | antd | +| `wrapperCol` | Field layout. The layout for input controls. Same as labelCol. | antd | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `labelClassName` | Label className. A custom className for the field's label. | bootstrap4, bootstrap5 | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | + +##### Props usage: + +```tsx +import { RadioField } from 'uniforms-unstyled'; + +; +``` + +### `SelectField` + +##### Props: + +| Name | Description | Available in | +| :---------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `appearance` | Field appearance. Set to "toggle" to appear as a Material Toggle or to "checkbox" (or leave it undefined) to use a Checkbox appearance. | material, mui | +| `checkboxes` | Turn on checkbox/radio mode. It's always true in multiple (i.e. fieldType === Array) mode. | All | +| `disableItem` | Disable items (options) based on a given predicate. | All | +| `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | +| `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | +| `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `inline` | Checkbox inline state. In bootstrap themes, a label is rendered as a text but in inline mode, it's treated as a field label. | bootstrap4, bootstrap5 | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `inputRef` | Setting ref prop to a field won't work as desired, because you'll receive a field component rather than an HTML input. If you need an input ref, use this prop instead. | All | +| `labelClassName` | Label className. A custom className for the field's label. | bootstrap4, bootstrap5 | +| `labelCol` | Field layout. The layout of label. You can set span and/or offset. | antd | +| `labelProps` | Props for the InputLabel | material, mui | +| `options` | Options. It can be either an object or an array (or a function, that returns it). | All | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | +| `wrapperCol` | Field layout. The layout for input controls. Same as labelCol. | antd | +| `wrapClassName` | Field and surroundings wrap className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `textFieldProps` | Props injected directly to `TextField` ( valid only for non-checkbox `SelectField` ). | material, mui | + +##### Props usage: + +```tsx +import { SelectField } from 'uniforms-unstyled'; +import { useRef } from 'react' + +const inputRef = useRef(); + + value % 2} + extra="Extra Feedback or Help" + help="Need help?" + helpClassName="a b c" + inline + inputClassName="a b c" + inputRef={inputRef} + labelClassName="a b c" // You can either specify them as a single string + labelClassName={[ 'a', 'b', 'c' ]} // or as an array of strings + labelCol={{offset: 2}} // 'ant-col-offset-2' on label + labelCol={{span: 4}} // 'ant-col-4' on label + labelProps={{shrink: true, disableAnimation: true}} + options={[{ label: 'A', value: 'a' }, { label: 'B', value: 'b' }, /* ... */]} + showInlineError + wrapperCol={{offset: 2}} // 'ant-col-offset-2' on field + wrapperCol={{span: 4}} // 'ant-col-4' on field +/>; +``` + +### `SubmitField` + +##### Props: + +| Name | Description | Available in | +| :--------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------: | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `inputRef` | Setting ref prop to a field won't work as desired, because you'll receive a field component rather than an HTML input. If you need an input ref, use this prop instead. | All | + +##### Props usage: + +```tsx +import { SubmitField } from 'uniforms-unstyled'; +import { useRef } from 'react'; + +const inputRef = useRef(); + +; +``` + +### `TextField` + +##### Props: + +| Name | Description | Available in | +| :---------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | +| `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | +| `grid` | Field layout. Bootstrap grid layout style. Number is an equivalent of n. Object is a size object. Complete string is simply passed through. | bootstrap4, bootstrap5 | +| `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | +| `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | +| `icon` | Input icon. Semantic inputs can have an icon. By default, it's placed on the right side - to place it on the left, use `iconLeft` prop instead. | semantic | +| `iconLeft` | Semantic inputs can have an icon. With this prop, it's placed on the left side - to place it on the right, use icon prop instead. | semantic | +| `iconProps` | Input icon props. Semantic inputs can have an icon. These props are passed directly to the icon element. | semantic | +| `inputClassName` | Input wrapper class name. In bootstrap themes, passed className is used on field block. This is used on direct field wrapper. | bootstrap4, bootstrap5 | +| `inputRef` | Setting ref prop to a field won't work as desired, because you'll receive a field component rather than an HTML input. If you need an input ref, use this prop instead. | All | +| `labelClassName` | Label className. A custom className for the field's label. | bootstrap4, bootstrap5 | +| `labelCol` | Field layout. The layout of label. You can set span and/or offset. | antd | +| `showInlineError` | Field inline error. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5, semantic | +| `type` | Input type. HTML compatible input type like password. Default is text. | All | +| `wrapperCol` | Field layout. The layout for input controls. Same as labelCol. | antd | +| `wrapClassName` | Field and surroundings wrap className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | + +##### Props usage: + +```tsx +import { TextField } from 'uniforms-unstyled'; +import { useRef } from 'react' + +const inputRef = useRef(); + +; +``` + +## Common props + +| Name | Default | Description | +| :-----------: | :-----: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| `disabled` | `false` | Field disabled state. It's passed directly to the field, but it propagates same as the label. | +| `label` | `true` | Field label. This prop has three modes. If you pass a string, then it will be used as a label. If you pass a null, then it won't have a label, but nested fields will have default labels. If you pass a non-null falsy value, it won't have a label and nested fields won't have labels too. | +| `name` | - | Field name. Used for identification. It should match your schema - if not, it will throw an error. | +| `placeholder` | `false` | Field placeholder. If set to true, then a label will be used. Otherwise, it's handled like a label (including propagation). | +| `readOnly` | `false` | Field read-only state. It's passed directly to the field, but it propagates same as the label. | + +##### Props usage: + +```tsx + +``` + +## Props propagation + +Few props propagate in a very special way. These are `disabled`, `label`, `placeholder`, and `readOnly`. + +**Example:** + +```tsx + // default label | no placeholder + // custom label | no placeholder + // no label | no placeholder + // default label | default placeholder + // default label | custom placeholder + + // null = no label but the children have their labels + + + + // false = no label and the children have no labels + + + + // Additions are disabled... + // ...deletion too + // ...but editing is not. + + + + + +``` + +**Note:** `disabled`, `label`, `placeholder`, and `readOnly` are casted to `Boolean` before being passed to nested fields. diff --git a/website/docs/api-reference/forms.mdx b/website/docs/api-reference/forms.mdx new file mode 100644 index 000000000..aaf646bc8 --- /dev/null +++ b/website/docs/api-reference/forms.mdx @@ -0,0 +1,462 @@ +--- +id: forms +title: 'Forms' +sidebar_position: 1 +--- + +## Forms components + +Most of the time you'll be using either `AutoForm` or `ValidatedForm`, but there are also other form components (rather low-level ones) with different capabilities. + +| Component | Self-generated? | Self-managed? | Self-validated? | +| :------------------: | :----------------------: | :----------------------: | :----------------------: | +| `AutoForm` | ✅ | ✅ | ✅ | +| `ValidatedQuickForm` | ✅ | ❌ | ✅ | +| `ValidatedForm` | ❌ | ❌ | ✅ | +| `QuickForm` | ✅ | ❌ | ❌ | +| `BaseForm` | ❌ | ❌ | ❌ | + +### `AutoForm` + +`AutoForm` extends `ValidatedQuickForm` with state management. +It is the most user-friendly and commonly used form. +It's self-generated so if you provide a schema, the fields will be automatically rendered. +These fields will be also validated. +By default, the validation will take place `onSubmit`, and `onChange` **after the first submission**. + +##### Props: + +| Name | Description | +| :-------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| `onChangeModel` | Like `onChange` but for the whole model. Triggered just after `onChange` with the next model and information what `{ key, value, previousValue }` caused the change. `previousValue` will be `undefined` if there was no value before. | + +**Note:** All `ValidatedQuickForm` props are also accepted and all methods are available. +In other words, that means that `AutoForm` receives all props listed on this page. + +##### Props usage: + +```tsx +import { AutoForm } from 'uniforms'; // Or from the theme package. + + console.log(model)} />; +``` + +### `ValidatedQuickForm` + +This form combines both `QuickForm` and `ValidatedForm` features. +It is not self-managed, however, it will automatically generate fields based on the provided schema and validate them. + +**Note:** All `QuickForm` props are also accepted and all methods are available.
+**Note:** All `ValidatedForm` props are also accepted and all methods are available. + +### `ValidatedForm` + +(_It's rather an internal form, but it's still exported._) + +It's based on `BaseForm` and extends its functionality by enabling automatic form validation. +Its purpose is providing validation functions. +It's not autogenerated, so if you want to see any fields rendered, you have to manually add them. +`ValidatedForm` is not self-managed, so you won't be able to type anything until there is no `onChange` handler, +however, there will be validation checks. + +##### Props: + +| Name | Description | +| :----------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| `onValidate` | Additional asynchronous validation. Schema validation has to be sync, so this is the only way to achieve async validation. | +| `validate` | Validation mode. By default, the form will start to validate from the time of the first submit and then revalidate on every change. It's `onChangeAfterSubmit`. There are also `onChange` and `onSubmit` modes, but those are quite self-explanatory. | +| `validator` | Validator options. It's passed to `getValidator` of your schema bridge. It really depends on your schema. | + +**Note:** All `BaseForm` props are also accepted and all methods are available. + +##### Props usage: + +```tsx +import { ValidatedForm } from 'uniforms'; // Or from the theme package. +import { useRef } from 'react'; + +const formRef = useRef(); + +const formAction = () => { + // Reset form. + // It will reset changed state, model state in AutoForm, validation + // state in ValidatedForm and rerender. + formRef.reset(); + + // Trigger form change. + // It's a programmatic equivalent of a change event. + formRef.change(key, value); + + // Submit form. + // It's a programmatic equivalent of a submit event. Returns a promise, + // which will either resolve with submitted form or reject with + // validation error in ValidatedForm. + formRef.submit(); +}; + + { + // You can either ignore validation error... + if (omitValidation(model)) { + return null; + } + + // ...or any additional validation if an error is already there... + if (isSomeSpecialCase(error)) { + return MyAPI.checkOtherCondition(model); + } + + // ...or feed it with another error. + return MyAPI.validate(model); + }} + validate="onChangeAfterSubmit" + validator={{ clean: true }} + ref={formRef} +/>; +``` + +### `QuickForm` + +(_It's rather an internal form, but it's still exported._) + +It's based on `BaseForm` and extends its functionality by enabling automatic form generation. +If you provide a schema, the fields will be automatically rendered. +However, `QuickForm` is not self-managed, so you won't be able to type anything until there is no `onChange` handler. +You can customize which `AutoField` should be used with [`AutoField.componentDetectorContext`](/docs/uth-autofield-algorithm#overriding-autofield). + +##### Props: + +| Name | Description | +| :-----------: | :---------------------------------------------------------------------------------------: | +| `errorsField` | Custom `ErrorsField`. It should be anything that will pass through `React.createElement`. | +| `submitField` | Custom `SubmitField`. It should be anything that will pass through `React.createElement`. | + +**Note:** All `BaseForm` props are also accepted and all methods are available. + +##### Props usage: + +```tsx +import { QuickForm } from 'uniforms'; // Or from the theme package. + +; +``` + +### `BaseForm` + +(_It's rather an internal form, but it's still exported._) + +It's the very basic form & foundation for the other forms. +It's not autogenerated, so if you want to see any fields rendered, you have to manually add them. +However, `BaseForm` is not self-managed, so you won't be able to type anything until there is no `onChange` handler. + +##### Props: + +| Name | Description | +| :---------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| `autosaveDelay` | Autosave delay. Set 0 for an instant autosave. | +| `autosave` | Enable autosave. Every change triggers `onSubmit`. | +| `disabled` | Default `disabled` prop for all fields. By default it's `false` - set it to `true` to disable the whole form. | +| `error` | Validation error. Current validation state. It should be either compatible with your schema or an `Error` object. | +| `grid` | Bootstrap grid layout style. Passing a number is an equivalent of `{sm: n}`. Object is a `{mode: size}` object. Complete string is simply passed through. Available in: bootstrap4. | +| `model` | Form model. An object with `{field: value}` structure. It doesn't matter if it has a prototype or not, but keep in mind that in `onSubmit` or in `onChangeModel` you'll receive a plain object. If you treat form as an input, then this is a value. | +| `modelTransform` | Model transform. Function transforming one model into another. It's used in a few situations (modes) described below. Do not mutate a given model! | +| `onChange` | Field change action. It receives two arguments: key and value, where the key is a dot-separated path to the changed field and value is a requested value. | +| `onSubmit` | Submit action. When the form is submitted manually or by an HTML5 event, then it's called with the current model. **Note:** use `Promise` to return values and errors - synchronous `return` and `throw` are disallowed. | +| `readOnly` | Default `readOnly` prop for all fields. By default it's `false` - set it to `true` to make the whole form read-only. | +| `schema` | Form schema. It's used for form generation in QuickForm and validation in ValidatedForm. | +| `showInlineError` | Default `showInlineError` prop for all fields. By default it's `false` - set it to `true` to enable inline errors for the whole form. Available in: antd, bootstrap4, semantic. | + +##### Props usage: + +```tsx +import { BaseForm } from 'uniforms'; // Or from the theme package. +import { useRef } from 'react'; + +const formRef = useRef(); + +const formAction = () => { + // Reset form. + // It will reset changed state, model state in AutoForm, validation + // state in ValidatedForm and rerender. + formRef.reset(); + + // Trigger form change. + // It's a programmatic equivalent of a change event. + formRef.change(key, value); + + // Submit form. + // It's a programmatic equivalent of a submit event. Returns a promise, + // which will either resolve with submitted form or reject with + // validation error in ValidatedForm. + formRef.submit(); +}; + + { + // This model will be passed to the fields. + if (mode === 'form') { + /* ... */ + } + + // This model will be submitted. + if (mode === 'submit') { + /* ... */ + } + + // This model will be validated. + if (mode === 'validate') { + /* ... */ + } + + // Otherwise, return unaltered model. + return model; + }} + onChange={(key, value) => console.log(key, value)} + onSubmit={model => db.saveThatReturnsPromiseOrNothing(model)} + readOnly={false} + schema={myFormSchema} + showInlineError + ref={formRef} +/>; +``` + +## Form features + +### Asynchronous validation + +`AutoForm` and `ValidatedForm` both accept an `onValidate` prop. It can be used to create an asynchronous validation: + +The `onValidate` should return `null` if the `model` is valid, otherwise return any error value. The error can be either `Promise` for asynchronous validation or any other value for synchronous validation (https://github.com/vazco/uniforms/blob/d557f90e6807e34c1ebb9803d44fd799174175f8/packages/uniforms/src/ValidatedForm.tsx#L118-L142). + +```tsx +const MyAPI = { + checkOtherCondition(model): Error | null { + if (model.age < 18) { + return new Error('Too young') + } + return null + } + + async validate(model): Error | null { + const result = await fetch('...', { body: JSON.stringify(model) }) + const { error } = await result.json() + return error + } +} + +const onValidate = async (model, error) => { + // You can either ignore validation error... + if (omitValidation(model)) { + return null; + } + + // ...or any additional validation if an error is already there... + if (isSomeSpecialCase(error)) { + return MyAPI.checkOtherCondition(model); + } + + // ...or feed it with another error. + return MyAPI.validate(model); +}; + +// Later... + +; +``` + +### Autosave + +Every form has autosave functionality. If you set an `autosave` prop, then every change will trigger a submit. There's also an `autosaveDelay` prop - a minimum time between saves in milliseconds (default: `0`). + +**Example:** + +```tsx + +``` + +### Methods + +You can use [React `ref` prop](https://facebook.github.io/react/docs/more-about-refs.html) to manually access form methods. Example usage: + +```tsx +import { useRef } from 'react'; + +const MyForm = ({ schema, onSubmit }) => { + const formRef = useRef(); + + return ( +
+ + formRef.reset()}>Reset + formRef.submit()}>Submit +
+ ); +}; +``` + +You can do the same by using the [`useForm`](/docs/api-context-data#accessing-context-data) hook and the [`formRef`](/docs/api-context-data#formref) property. + +```tsx +function FormControls() { + const { formRef } = useForm(); + + return ( + <> + + + + ); +} + +function App() { + return ( + + + + ); +} +``` + +All available methods: + +#### `change(key, value)` + +Triggers a form change. It's a programmatic equivalent of a change event. + +#### `reset()` + +Resets a form. It will also reset changed state, model state (only in AutoForm), validation state (only in ValidatedForm) and trigger a rerender. + +#### `submit()` + +Submits a form. It's a programmatic equivalent of a submit event. Returns a promise, which will either resolve with a submitted model or reject with validation error in ValidatedForm. + +#### `validate()` + +_(added in `ValidatedForm`)_ + +Validates a form with the current model. Returns a Promise, which rejects with a validation error or resolves without any value. Note, that it resolves/rejects **after** the component is rerendered. + +#### `validate(key, value)` + +_(added in `ValidatedForm`)_ + +Validates a form with key set to value. You can use it to check, if a given value will pass the validation or not. Returns validation Promise, as described above. + +#### `validateModel(model)` + +_(added in `ValidatedForm`)_ + +Validates a form with the given model. Returns validation Promise, as described above. + +### Change reactions + +If you want to make one field to influence others, simply extend `AutoForm` and override `onChange` method. + +**Example:** + +```tsx +class ChainForm extends AutoForm { + onChange(key, value) { + if (key === 'key_to_intercept') return; + if (key === 'key_to_translate') return super.onChange('another_key', value); + if (key === 'key_to_mutate') { + super.onChange('another_key1', value * 2); + super.onChange('another_key2', value / 2); + return; + } + + super.onChange(key, value); + } +} +``` + +It can be easily applied multiple times to make your forms even more reusable. + +**Example:** + +```tsx +const withMultipliedField = (fieldA, fieldB, Form) => + class withMultipliedFieldForm extends Form { + onChange(key, value) { + // Multiply fieldA + if (key === fieldA) super.onChange(fieldB, value + value); + + // Pass every change + super.onChange(key, value); + } + }; +``` + +### Model transformations + +If you need to transform model before it will be validated, submitted or passed down to the fields, there's a `modelTransform` prop, which should be used in those use cases. + +**Example:** + +```tsx + { + // This model will be passed to the fields. + if (mode === 'form') { + /* ... */ + } + + // This model will be submitted. + if (mode === 'submit') { + /* ... */ + } + + // This model will be validated. + if (mode === 'validate') { + /* ... */ + } + + // Otherwise, return unaltered model. + return model; + }} + onSubmit={onSubmit} + schema={schema} +/> +``` + +### Validation options and modes + +Any form can be validated in one those three styles: + +- `onChange` + Validate on every change. + +- `onChangeAfterSubmit` _(default)_ + Validate on every change, but only after first submit. + +- `onSubmit` + Validate on every submit. + +If your schema validator accepts any options, those can be passed in `validator` prop. + +**Example:** + +```tsx + +``` diff --git a/website/docs/api-reference/helpers.mdx b/website/docs/api-reference/helpers.mdx new file mode 100644 index 000000000..ceffa3cef --- /dev/null +++ b/website/docs/api-reference/helpers.mdx @@ -0,0 +1,217 @@ +--- +id: helpers +title: Helpers +sidebar_position: 5 +--- + +## `connectField` + +Provides form management related props. The `connectField` helper is a component wrapper (higher order component, HOC), that provides various props related to the form management. It also adds the `Field` suffix to the name of the wrapped component. + +The table below lists all of the **guaranteed** props that will be passed to the wrapped component: + +| Name | Type | Description | +| :---------------: | :-------------------: | :------------------------------------: | +| `changed` | `boolean` | Has field changed? | +| `disabled` | `boolean` | Is field disabled? | +| `error` | `object` | Field scoped part of validation error. | +| `errorMessage` | `string` | Field scoped validation error message. | +| `field` | `object` | Field definition from schema. | +| `fields` | `arrayOf(string)` | Subfields names. | +| `fieldType` | `func` | Field type. | +| `id` | `string` | Field id - given or random. | +| `label` | `string` | Field label. | +| `name` | `string` | Field name. | +| `onChange` | `func(value, [name])` | Change field value. | +| `placeholder` | `string` | Field placeholder. | +| `readOnly` | `boolean` | Is field read-only? | +| `showInlineError` | `boolean` | Show inline error? | +| `value` | `any` | Field value. | + +The `connectField` function accepts two arguments: the first one is a component and the second one is an `options` object. + +```tsx +function Example(props) { + /* ... */ +} + +const ExampleField = connectField(Example, options); +``` + +The table below lists all available options: + +| Name | Type | Description | +| :------------: | :------------------: | :----------------------------------------------------------------------------------------------------------------------------------: | +| `initialValue` | `boolean` | Initial value check. If `true`, then after the first render the default value is set as value if no value is provided (`undefined`). | +| `kind` | `'leaf'` or `'node'` | See [Field kinds](#field-kinds). | + +### Field kinds + +Every field is either a _leaf_ or _node_ field. In the future, we could introduce new kinds to enable even more optimizations. + +- _Leaf_ fields cannot have subfields. This allows us to perform some optimizations, like skipping the extra `Provider` from `connectField`, effectively reducing the overhead down to a single `useField` call. + - It includes all input fields, like `NumField`, `SelectField` or `TextField`. +- _Node_ fields can have subfields. Fields of the _leaf_ kind cannot have subfields. + - It includes all combined and layout fields, like `ListField` or `NestField`. + +If you are not sure which one to use, do not use the `kind` option at all - it'll default to the safest option (right now it's `node`). + +### Props merging order + +The resulting props of a field are a merge of the props that uniforms provide, the ones coming from the bridge (schema) and finally the actual field props. For the exact ordering, please refer to the [source of `useField` hook](https://github.com/vazco/uniforms/blob/master/packages/uniforms/src/useField.tsx). Overall, it looks as follows: + +1. uniforms props (e.g., `changed`, `onChange`, `value`). +2. uniforms state (as defined in [context data](/docs/api-context-data/#state)). +3. bridge props (depending on the schema) +4. field props (only when rendered directly) + +That's important, as using empty values in the schema or field props, like `undefined`, will be merged as well. See [#1094](https://github.com/vazco/uniforms/issues/1094) for more context as well as an example of a potential pitfall. + +## `changedKeys` + +Returns an array of changed keys between `valueA` and `valueB`, where `root` is the root key. For examples see [`changedKeys` tests](https://github.com/vazco/uniforms/blob/master/packages/uniforms/__tests__/changedKeys.ts). + +```tsx +import { changedKeys } from 'uniforms'; + +changedKeys('a', { b: 1, c: 2 }, { b: 1 }); // ['a', 'a.c'] +``` + +## `filterDOMProps` + +Removes all uniforms-related props, registered with `filterDOMProps.register`. Use it in all places where you'd like to pass all unrelated props down and `useField` or `connectField` provide you with the props. + +```tsx +import { filterDOMProps } from 'uniforms'; + +const filteredProps = filterDOMProps(props); +``` + +### Custom props registration + +It's often the case that your custom components will have a bunch of known properties, like `locale` or `userType`. To ease the process of using them across the project, you can register them to make `filterDOMProps` remove them as well. For example, [`SimpleSchemaBridge`](https://github.com/vazco/uniforms/blob/master/packages/uniforms-bridge-simple-schema/src/register.ts) registers all of the SimpleSchema-specific options. + +```tsx +import { filterDOMProps } from 'uniforms'; + +filterDOMProps({ example: 42 }); // { example: 42 } +filterDOMProps.registered.includes('example'); // false +filterDOMProps.register('example'); +filterDOMProps.registered.includes('example'); // true +filterDOMProps({ example: 42 }); // {} +``` + +As `filterDOMProps` is fully typed, if you'd like to make it work with TypeScript, you have to extend the `FilterDOMProps` interface as well. + +```tsx +declare module 'uniforms' { + interface FilterDOMProps { + propA: never; + propB: never; + } +} + +filterDOMProps.register('propA', 'propB'); +``` + +## `joinName` + +Safely joins partial field names. +If you create a custom field with subfields, do use `joinName` instead of manually concatenating them. +It ensures that the name will be correctly escaped if needed. + +```tsx +import { joinName } from 'uniforms'; + +joinName('array', 1, 'field'); // 'array.1.field' +joinName('object', 'nested.property'); // 'object.nested.property' +``` + +If the first argument is `null`, then it returns an array of escaped parts. + +```tsx +import { joinName } from 'uniforms'; + +joinName(null, 'array', 1, 'field'); // ['array', '1', 'field'] +joinName(null, 'object', 'nested.property'); // ['object', 'nested', 'property'] +``` + +If the field name contains a dot (`.`) or a bracket (`[` or `]`), it has to be escaped with `["..."]`. +If any of these characters is not escaped, `joinName` will **not** throw an error but its behavior is not specified. +The escape of any other name part will be stripped. + +```tsx +joinName(null, 'object["with.dot"].field'); // ['object', '["with.dot"]', 'field'] +joinName('object["with.dot"].field'); // 'object["with.dot"].field' + +joinName(null, 'this["is"].safe'); // ['this', 'is', 'safe'] +joinName('this["is"].safe'); // 'this.is.safe' +``` + +For more examples check [`joinName` tests](https://github.com/vazco/uniforms/blob/master/packages/uniforms/__tests__/joinName.ts). + +## `randomIds` + +Generates random ID, based on given prefix. Use it, if you want to have random but deterministic strings. If no prefix is provided, a unique `uniforms-X` prefix will be used generated. + +```tsx +import { randomIds } from 'uniforms'; + +const randomId1 = randomIds(); +randomId1(); // uniforms-0000-0000 +randomId1(); // uniforms-0000-0001 +randomId1(); // uniforms-0000-0002 + +const randomId2 = randomIds(); +randomId2(); // uniforms-0001-0000 +randomId2(); // uniforms-0001-0001 +randomId2(); // uniforms-0001-0002 + +const randomId3 = randomIds('prefix'); +randomId3(); // prefix-0000 +randomId3(); // prefix-0001 +randomId3(); // prefix-0002 +``` + +## `useForm` + +A direct way of accessing the [context data](/docs/api-context-data/#state): + +```tsx +import { useForm } from 'uniforms'; + +function Example() { + const context = useForm(); +} +``` + +## `useField` + +A hook version of [`connectField`](#connectfield). It receives three arguments: field name (`string`), field props (`object`), and optional options. + +```tsx +function Example(props) { + const [fieldProps, context] = useField(props.name, props, options); + return ; +} +``` + +The table below lists all available options: + +| Name | Type | Default | Description | +| :------------: | :-------: | :-----: | :----------------------------------------------------------------------------------------------------------------------------------: | +| `absoluteName` | `boolean` | `false` | Whether the field name should be treated as a top-level one, ignoring parent fields. | +| `initialValue` | `boolean` | `true` | Initial value check. If `true`, then after the first render the default value is set as value if no value is provided (`undefined`). | + +Using `useField` allows you to create components that combine values of multiple fields: + +```tsx +import { useField } from 'uniforms'; + +function ArePasswordsEqual() { + const [{ value: passwordA }] = useField('passwordA', {}); + const [{ value: passwordB }] = useField('passwordB', {}); + const areEqual = passwordA === passwordB; + return
{`Passwords are ${areEqual ? 'equal' : 'not equal'}`}
; +} +``` From 442fef16a3fd1f231b82131f4ea72b2b0e407a96 Mon Sep 17 00:00:00 2001 From: Volodymyr Zakhovaiko Date: Fri, 17 Jan 2025 13:54:18 +0100 Subject: [PATCH 2/4] Removed grid props from BaseForm and info about deprecations --- website/docs/api-reference/bridges.mdx | 121 ------------------------- website/docs/api-reference/forms.mdx | 1 - 2 files changed, 122 deletions(-) diff --git a/website/docs/api-reference/bridges.mdx b/website/docs/api-reference/bridges.mdx index b76ef3a28..c13586400 100644 --- a/website/docs/api-reference/bridges.mdx +++ b/website/docs/api-reference/bridges.mdx @@ -153,33 +153,6 @@ const schema = z.object({ aboutMe: z.string() }); const bridge = new ZodBridge({ schema }); ``` -## `SimpleSchemaBridge` - -:::caution - -SimpleSchemaBridge is deprecated since uniforms v4. - -::: - -```tsx -import SimpleSchemaBridge from 'uniforms-bridge-simple-schema'; -import { SimpleSchema } from 'aldeed:simple-schema'; - -const schema = new SimpleSchema({ - // ... - - aboutMe: { - type: String, - uniforms: MyText, // Component... - uniforms: { - // ...or object... - component: MyText, // ...with component... - propA: 1, // ...and/or extra props. - }, - }, -}); - -const bridge = new SimpleSchemaBridge({ schema }); ``` ## `ZodBridge` @@ -192,97 +165,3 @@ const schema = z.object({ aboutMe: z.string() }); const bridge = new ZodBridge({ schema }); ``` - -## `GraphQLBridge` - -:::caution - -GraphQLBridge is deprecated since uniforms v4. - -::: - -This bridge enables using GraphQL schema types as uniforms forms. -This saves you from not having to rewrite the form schema in your code. -As a trade-off, you have to write the validator from scratch. In some cases, it might be easier to rewrite the schema and use, for example, [`JSONSchemaBridge` with `ajv`](/docs/api-bridges#jsonschemabridge). -If only a simple or no validation is needed, this bridge is perfectly suited to work with GraphQL schemas. - -The constructor accepts these arguments: - -- `schema: GraphQLType` can be any type parsed and extracted from a GraphQL schema. -- `validator: (model: Record) => any` a custom validator function that should return a falsy value if no errors are present or information about errors in the model as described in the [custom bridge section](/docs/examples-custom-bridge#validator-definition). -- `extras: Record = {}` used to extend the schema generated from GraphQL type with extra field configuration. -- `provideDefaultLabelFromFieldName = true` if set to `true`, the bridge will use the field name as a label if no label is provided in the schema. - -### Code example - -```tsx -import { GraphQLBridge } from 'uniforms-bridge-graphql'; -import { buildASTSchema, parse } from 'graphql'; - -const schema = ` - type Author { - id: String! - firstName: String - lastName: String - } - - type Post { - id: Int! - author: Author! - title: String - votes: Int - } - - # This is required by buildASTSchema - type Query { anything: ID } -`; - -const schemaType = buildASTSchema(parse(schema)).getType('Post'); -const schemaExtras = { - id: { - options: [ - { label: 1, value: 1 }, - { label: 2, value: 2 }, - { label: 3, value: 3 }, - ], - }, - title: { - options: [ - { label: 1, value: 'a' }, - { label: 2, value: 'b' }, - ], - }, - 'author.firstName': { - placeholder: 'John', - }, -}; - -const schemaValidator = (model: object) => { - const details = []; - - if (!model.id) { - details.push({ name: 'id', message: 'ID is required!' }); - } - - if (!model.author.id) { - details.push({ name: 'author.id', message: 'Author ID is required!' }); - } - - if (model.votes < 0) { - details.push({ - name: 'votes', - message: 'Votes must be a non-negative number!', - }); - } - - // ... - - return details.length ? { details } : null; -}; - -const bridge = new GraphQLBridge({ - schema: schemaType, - validator: schemaValidator, - extras: schemaExtras, -}); -``` diff --git a/website/docs/api-reference/forms.mdx b/website/docs/api-reference/forms.mdx index aaf646bc8..e97ccc0e4 100644 --- a/website/docs/api-reference/forms.mdx +++ b/website/docs/api-reference/forms.mdx @@ -157,7 +157,6 @@ However, `BaseForm` is not self-managed, so you won't be able to type anything u | `autosave` | Enable autosave. Every change triggers `onSubmit`. | | `disabled` | Default `disabled` prop for all fields. By default it's `false` - set it to `true` to disable the whole form. | | `error` | Validation error. Current validation state. It should be either compatible with your schema or an `Error` object. | -| `grid` | Bootstrap grid layout style. Passing a number is an equivalent of `{sm: n}`. Object is a `{mode: size}` object. Complete string is simply passed through. Available in: bootstrap4. | | `model` | Form model. An object with `{field: value}` structure. It doesn't matter if it has a prototype or not, but keep in mind that in `onSubmit` or in `onChangeModel` you'll receive a plain object. If you treat form as an input, then this is a value. | | `modelTransform` | Model transform. Function transforming one model into another. It's used in a few situations (modes) described below. Do not mutate a given model! | | `onChange` | Field change action. It receives two arguments: key and value, where the key is a dot-separated path to the changed field and value is a requested value. | From 07d30404fd55cb3e3af300536b1c18c9e5bed234 Mon Sep 17 00:00:00 2001 From: Volodymyr Zakhovaiko Date: Fri, 17 Jan 2025 13:56:42 +0100 Subject: [PATCH 3/4] Removed grid props from Fields page --- website/docs/api-reference/fields.mdx | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/website/docs/api-reference/fields.mdx b/website/docs/api-reference/fields.mdx index 0a62c0155..b661d06aa 100644 --- a/website/docs/api-reference/fields.mdx +++ b/website/docs/api-reference/fields.mdx @@ -92,7 +92,6 @@ A checkbox. | :---------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | | `appearance` | Field appearance. Set to "toggle" to appear as a Material Toggle or to "checkbox" (or leave it undefined) to use a Checkbox appearance. | material, mui | | `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | -| `grid` | Field layout. Bootstrap grid layout style. Number is an equivalent of n. Object is a size object. Complete string is simply passed through. | bootstrap4, bootstrap5 | | `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | | `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | | `inline` | Checkbox inline state. In bootstrap themes, a label is rendered as a text but in inline mode, it's treated as a field label. | bootstrap4, bootstrap5 | @@ -117,10 +116,6 @@ const inputRef = useRef(); appearance="checkbox" // Renders a material-ui Checkbox appearance="toggle" // Renders a material-ui Toggle extra="Extra Feedback or Help" - grid="4" // 'col-4-sm' on label, 'col-8-sm' on input - grid="col-6-xl" // 'col-6-xl' on label, 'col-6-xl' on input - grid={3} // 'col-3-sm' on label, 'col-9-sm' on input - grid={{md: 5}} // 'col-5-md' on label, 'col-7-md' on input help="Need help?" helpClassName="a b c" inline @@ -145,7 +140,6 @@ const inputRef = useRef(); | Name | Description | Available in | | :---------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------: | | `extra` | Extra feedback text. In the antd theme, this renders additional help text below any validation messages. | antd | -| `grid` | Field layout. Bootstrap grid layout style. Number is an equivalent of n. Object is a size object. Complete string is simply passed through. | bootstrap4, bootstrap5 | | `help` | Help text. _Some description would be great, huh?_ | antd, bootstrap4, bootstrap5 | | `helpClassName` | Help block className. _Some description would be great, huh?_ | bootstrap4, bootstrap5 | | `icon` | Input icon. Semantic inputs can have an icon. By default, it's placed on the right side - to place it on the left, use `iconLeft` prop instead. | semantic | @@ -174,10 +168,6 @@ const inputRef = useRef(); Date: Fri, 17 Jan 2025 14:04:32 +0100 Subject: [PATCH 4/4] Fixed links --- README.md | 2 +- website/docs/api-reference/bridges.mdx | 2 +- website/docs/api-reference/context-data.mdx | 8 ++++---- website/docs/api-reference/forms.mdx | 2 +- website/docs/api-reference/helpers.mdx | 4 ++-- website/src/pages-parts/LandingPage/Footer.tsx | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index cd0b05a9b..0faef8ceb 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

- API | + API | Contribute | Quick Start | Playground | diff --git a/website/docs/api-reference/bridges.mdx b/website/docs/api-reference/bridges.mdx index c13586400..62f1f0dc4 100644 --- a/website/docs/api-reference/bridges.mdx +++ b/website/docs/api-reference/bridges.mdx @@ -21,7 +21,7 @@ Deprecated bridges: - `SimpleSchemaBridge` in `uniforms-bridge-simple-schema` ([schema documentation](https://github.com/Meteor-Community-Packages/meteor-simple-schema/blob/master/DOCS.md)) - `GraphQLBridge` in `uniforms-bridge-graphql` ([schema documentation](https://graphql.org/)) -If you see a lot of [`Warning: Unknown props...`](https://fb.me/react-unknown-prop) logs, check if your schema or theme doesn't provide extra props. If so, consider [registering it with `filterDOMProps`](/docs/api-helpers#filterdomprops). +If you see a lot of [`Warning: Unknown props...`](https://fb.me/react-unknown-prop) logs, check if your schema or theme doesn't provide extra props. If so, consider [registering it with `filterDOMProps`](/docs/api-reference/helpers#filterdomprops). ## `JSONSchemaBridge` diff --git a/website/docs/api-reference/context-data.mdx b/website/docs/api-reference/context-data.mdx index 6fcfd7029..74cb48b7c 100644 --- a/website/docs/api-reference/context-data.mdx +++ b/website/docs/api-reference/context-data.mdx @@ -11,7 +11,7 @@ Some of them were designed for internal use, but you can still take advantage of ## Accessing context data -A direct way of accessing the context data is to use the [`useForm`](/docs/api-helpers/#useform) hook: +A direct way of accessing the context data is to use the [`useForm`](/docs/api-reference/helpers#useform) hook: ```tsx import { useForm } from 'uniforms'; @@ -21,7 +21,7 @@ function Example() { } ``` -For convenience, it's also accessible through the [`useField`](/docs/api-helpers/#usefield) hook: +For convenience, it's also accessible through the [`useField`](/docs/api-reference/helpers#usefield) hook: ```tsx import { useField } from 'uniforms'; @@ -47,7 +47,7 @@ An object with a `details` field, which is an array of any validation errors. ### `formRef` -Contains reference to the form component that gives access to [the form methods](/docs/api-forms#methods). +Contains reference to the form component that gives access to [the form methods](/docs/api-reference/forms#methods). ### `model` @@ -99,7 +99,7 @@ Access to `onSubmit` method. ### `randomId` -Access to `randomId` method, created using the [`randomIds()`](/docs/api-helpers#randomids) helper. +Access to `randomId` method, created using the [`randomIds()`](/docs/api-reference/helpers#randomids) helper. ### `schema` diff --git a/website/docs/api-reference/forms.mdx b/website/docs/api-reference/forms.mdx index e97ccc0e4..c9fc787f6 100644 --- a/website/docs/api-reference/forms.mdx +++ b/website/docs/api-reference/forms.mdx @@ -307,7 +307,7 @@ const MyForm = ({ schema, onSubmit }) => { }; ``` -You can do the same by using the [`useForm`](/docs/api-context-data#accessing-context-data) hook and the [`formRef`](/docs/api-context-data#formref) property. +You can do the same by using the [`useForm`](/docs/api-reference/context-data#accessing-context-data) hook and the [`formRef`](/docs/api-reference/context-data#formref) property. ```tsx function FormControls() { diff --git a/website/docs/api-reference/helpers.mdx b/website/docs/api-reference/helpers.mdx index ceffa3cef..6f6c51928 100644 --- a/website/docs/api-reference/helpers.mdx +++ b/website/docs/api-reference/helpers.mdx @@ -61,7 +61,7 @@ If you are not sure which one to use, do not use the `kind` option at all - it'l The resulting props of a field are a merge of the props that uniforms provide, the ones coming from the bridge (schema) and finally the actual field props. For the exact ordering, please refer to the [source of `useField` hook](https://github.com/vazco/uniforms/blob/master/packages/uniforms/src/useField.tsx). Overall, it looks as follows: 1. uniforms props (e.g., `changed`, `onChange`, `value`). -2. uniforms state (as defined in [context data](/docs/api-context-data/#state)). +2. uniforms state (as defined in [context data](/docs/api-reference/context-data/#state)). 3. bridge props (depending on the schema) 4. field props (only when rendered directly) @@ -175,7 +175,7 @@ randomId3(); // prefix-0002 ## `useForm` -A direct way of accessing the [context data](/docs/api-context-data/#state): +A direct way of accessing the [context data](/docs/api-reference/context-data/#state): ```tsx import { useForm } from 'uniforms'; diff --git a/website/src/pages-parts/LandingPage/Footer.tsx b/website/src/pages-parts/LandingPage/Footer.tsx index b37b45553..25c14f8f0 100644 --- a/website/src/pages-parts/LandingPage/Footer.tsx +++ b/website/src/pages-parts/LandingPage/Footer.tsx @@ -32,7 +32,7 @@ export function Footer() {

  • Examples
  • - +
  • API Reference