Skip to content

Commit

Permalink
partial fix: Added FormContextType and extending the F generic from it (
Browse files Browse the repository at this point in the history
#3220)

- partial fix for #3072 by adding the new `FormContextType` and extending the `F` generic from it
- Updated `@rjsf/utils` to add the new `FormContextType` and update all types and functions to extend the `F` generic from it
- Updated `@rjsf/core` to update all uses of the `F` generic to extend `FormContextType`
- Updated the documentation around `F` accordingly
  - Also fixed an issue where the `<S` notation in Markdown caused strike-through formatting by changing it to `\<S`
- Updated the `CHANGELOG.md` accordingly
  • Loading branch information
heath-freenome committed Oct 31, 2022
1 parent 8085aab commit c78a1c9
Show file tree
Hide file tree
Showing 66 changed files with 317 additions and 155 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -32,6 +32,7 @@ should change the heading of the (upcoming) version to include a major version b
- Added the new generic, `S extends StrictRJSFSchema = RJSFSchema`, for `schema`/`rootSchema` to every component that needed it.
- Fix omitExtraData with field names with dots #2643
- Updated the tests to use the `@rjsf/validator-ajv8` fixing [#3110](https://github.com/rjsf-team/react-jsonschema-form/issues/3110)
- Changed the `F = any` generic to be `F extends FormContextType = any` to better support how `formContext` is defined and used, partially fixing [#3072](https://github.com/rjsf-team/react-jsonschema-form/issues/3072)

## @rjsf/fluent-ui
- Updated the tests to use the `@rjsf/validator-ajv8` fixing [#3110](https://github.com/rjsf-team/react-jsonschema-form/issues/3110)
Expand All @@ -51,6 +52,7 @@ should change the heading of the (upcoming) version to include a major version b
- This new generic was added BEFORE the newly added `F = any` generic because it is assumed that more people will want to change the schema than the formContext types
- This provides future support for the newer draft versions of the schema
- Updated the `ValidatorType` interface to add a new `rawValidation()` method for use by the playground
- Added the `FormContextType` alias to `GenericObjectType` and changing the `F = any` generic to be `F extends FormContextType = any` to better support how `formContext` is defined and used, partially fixing [#3072](https://github.com/rjsf-team/react-jsonschema-form/issues/3072)

## @rjsf/validator-ajv6
- Fixed a few type casts given the new expanded definition of the `RJSFSchema` type change
Expand All @@ -63,7 +65,7 @@ should change the heading of the (upcoming) version to include a major version b
- Refactored out the `rawValidation()` function for use by the playground

## Dev / docs / playground
- Updated the `5.x upgrade guide` and `utility-functions.md` to document the new `StrictRJSFSchema` and `S` generic
- Updated the `5.x upgrade guide` and `utility-functions.md` to document the new `StrictRJSFSchema`, the `S` generic and changing the `F` generic extend
- Updated the `validation` guide to document the new `AjvClass` prop on `CustomValidatorOptionsType` and mentioning the deprecation of `@rjsf/validator-ajv6`
- Updated the playground to add support for using the AJV 8 validator with the `draft-2019-09` and `draft-2020-12` schemas and to make the `AJV8` validator the default validator, marking `AJV6` as deprecated
- Updated all the documentation to switch to Typescript notation where missing along with switching to using the `@rjsf/validator-ajv8` validator as the default
Expand Down
2 changes: 1 addition & 1 deletion docs/5.x upgrade guide.md
Expand Up @@ -52,7 +52,7 @@ Some of the most notable changes are:
- `RJSFSchema` is `StrictRJSFSchema` joined with the `GenericObjectType` (i.e. `{ [key: string]: any }`) to allow for additional syntax related to newer draft versions
- All definitions of `schema` and `rootSchema` elements have been replaced with a generic that is defined as `S extends StrictRJSFSchema = RJSFSchema`
- It is highly recommended to update your use of `JSON7Schema` with `RJSFSchema` since that is the default for the new generic used for `schema` and `rootSchema`
- A new generic `F` (defaulting to `any`) was added for the `formContext` type, and all types in the hierarchy that use `formContext` have had that generic added to them.
- A new generic `F` (extending `FormContextType` defaulting to `any`) was added for the `formContext` type, and all types in the hierarchy that use `formContext` have had that generic added to them.
- The new `CustomValidator`, `ErrorTransformer`, `ValidationData`, `ValidatorType` and `SchemaUtilsType` types were added to support the decoupling of the validation implementation.
- The new `TemplatesType`, `ArrayFieldDescriptionProps`, `ArrayFieldTitleProps`, `UnsupportedFieldProps`, `IconButtonProps`, `SubmitButtonProps` and `UIOptionsBaseType` were added to support the consolidation (and expansion) of `templates` in the `Registry` and `Form`.
- **BREAKING CHANGE** The `DescriptionField` and `TitleField` props were removed from the `ArrayFieldTemplateProps` and `ObjectFieldTemplateProps` as they can now be derived from the `templates` or `uiSchema` via the new `getTemplate()` utility function.
Expand Down
39 changes: 20 additions & 19 deletions docs/api-reference/utility-functions.md
Expand Up @@ -43,7 +43,7 @@ Otherwise, the string is wrapped by `Number()` and if that result is not `NaN`,
#### Returns
- undefined | null | string | number: The `value` converted to a number when appropriate, otherwise the `value`

### canExpand<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### canExpand<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()

Checks whether the field described by `schema`, having the `uiSchema` and `formData` supports expanding.
The UI for the field can expand if it has additional properties, is not forced as non-expandable by the `uiSchema` and the `formData` object doesn't already have `schema.maxProperties` elements.
Expand Down Expand Up @@ -76,7 +76,8 @@ Implements a deep equals using the `lodash.isEqualWith` function, that provides
#### Returns
- boolean: True if the `a` and `b` are deeply equal, false otherwise

### findSchemaDefinition<S extends StrictRJSFSchema = RJSFSchema>()
### findSchemaDefinition\<S extends StrictRJSFSchema = RJSFSchema>()

Given the name of a `$ref` from within a schema, using the `rootSchema`, look up and return the sub-schema using the path provided by that reference.
If `#` is not the first character of the reference, or the path does not exist in the schema, then throw an Error.
Otherwise return the sub-schema. Also deals with nested `$ref`s in the sub-schema.
Expand All @@ -91,8 +92,8 @@ Otherwise return the sub-schema. Also deals with nested `$ref`s in the sub-schem
#### Throws
- Error indicating that no schema for that reference exists

### getInputProps<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
Using the `schema`, `defaultType` and `options`, extract out the props for the <input> element that make sense.
### getInputProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Using the `schema`, `defaultType` and `options`, extract out the props for the `<input>` element that make sense.

#### Parameters
- schema: S - The schema for the field provided by the widget
Expand All @@ -117,7 +118,7 @@ If the type is not explicitly defined, then an attempt is made to infer it from
#### Returns
- string | string[] | undefined: The type of the schema

### getSubmitButtonOptions<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### getSubmitButtonOptions<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Extracts any `ui:submitButtonOptions` from the `uiSchema` and merges them onto the `DEFAULT_OPTIONS`

#### Parameters
Expand All @@ -126,7 +127,7 @@ Extracts any `ui:submitButtonOptions` from the `uiSchema` and merges them onto t
#### Returns
- UISchemaSubmitButtonOptions: The merging of the `DEFAULT_OPTIONS` with any custom ones

### getUiOptions<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### getUiOptions<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Get all passed options from ui:options, and ui:<optionName>, returning them in an object with the `ui:` stripped off.

#### Parameters
Expand All @@ -135,7 +136,7 @@ Get all passed options from ui:options, and ui:<optionName>, returning them in a
#### Returns
- UIOptionsType<T, S, F> An object containing all of the `ui:xxx` options with the stripped off

### getTemplate<Name extends keyof TemplatesType<T, S, F>, T = any, F = any>()
### getTemplate<Name extends keyof TemplatesType<T, S, F>, T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Returns the template with the given `name` from either the `uiSchema` if it is defined or from the `registry`
otherwise. NOTE, since `ButtonTemplates` are not overridden in `uiSchema` only those in the `registry` are returned.

Expand All @@ -147,7 +148,7 @@ otherwise. NOTE, since `ButtonTemplates` are not overridden in `uiSchema` only t
#### Returns
- TemplatesType<T, S, F>[Name] - The template from either the `uiSchema` or `registry` for the `name`

### getWidget<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### getWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Given a schema representing a field to render and either the name or actual `Widget` implementation, returns the
React component that is used to render the widget. If the `widget` is already a React component, then it is wrapped
with a `MergedWidget`. Otherwise an attempt is made to look up the widget inside of the `registeredWidgets` map based
Expand All @@ -174,7 +175,7 @@ create a schema, it is useful to know what type to use based on the data we are
#### Returns
- string: The best guess for the object type

### hasWidget<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### hasWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Detects whether the `widget` exists for the `schema` with the associated `registryWidgets` and returns true if it does, or false if it doesn't.

#### Parameters
Expand All @@ -185,7 +186,7 @@ Detects whether the `widget` exists for the `schema` with the associated `regist
#### Returns
- boolean: True if the widget exists, false otherwise

### isConstant<S extends StrictRJSFSchema = RJSFSchema>()
### isConstant\<S extends StrictRJSFSchema = RJSFSchema>()
This function checks if the given `schema` matches a single constant value.
This happens when either the schema has an `enum` array with a single value or there is a `const` defined.

Expand All @@ -195,7 +196,7 @@ This happens when either the schema has an `enum` array with a single value or t
#### Returns
- boolean: True if the `schema` has a single constant value, false otherwise

### isCustomWidget<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### isCustomWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Checks to see if the `uiSchema` contains the `widget` field and that the widget is not `hidden`

#### Parameters
Expand All @@ -204,7 +205,7 @@ Checks to see if the `uiSchema` contains the `widget` field and that the widget
#### Returns
- boolean: True if the `uiSchema` describes a custom widget, false otherwise

### isFixedItems<S extends StrictRJSFSchema = RJSFSchema>()
### isFixedItems\<S extends StrictRJSFSchema = RJSFSchema>()
Detects whether the given `schema` contains fixed items.
This is the case when `schema.items` is a non-empty array that only contains objects.

Expand Down Expand Up @@ -272,7 +273,7 @@ The difference between mergeSchemas and mergeObjects is that mergeSchemas only c
#### Returns
- GenericObjectType: The merged schema object

### optionsList<S extends StrictRJSFSchema = RJSFSchema>()
### optionsList\<S extends StrictRJSFSchema = RJSFSchema>()
Gets the list of options from the schema. If the schema has an enum list, then those enum values are returned.
The labels for the options will be extracted from the non-standard `enumNames` if it exists otherwise will be the same as the `value`.
If the schema has a `oneOf` or `anyOf`, then the value is the list of `const` values from the schema and the label is either the `schema.title` or the value.
Expand Down Expand Up @@ -324,7 +325,7 @@ Parses the `dateString` into a `DateObject`, including the time information when
#### Throws
- Error when the date cannot be parsed from the string

### processSelectValue<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### processSelectValue<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Returns the real value for a select widget due to a silly limitation in the DOM which causes option change event values to always be retrieved as strings.
Uses the `schema` to help determine the value's true type.
If the value is an empty string, then the `emptyValue` from the `options` is returned, falling back to undefined.
Expand All @@ -337,7 +338,7 @@ If the value is an empty string, then the `emptyValue` from the `options` is ret
#### Returns
- string | boolean | number | string[] | boolean[] | number[] | undefined: The `value` converted to the proper type

### rangeSpec<S extends StrictRJSFSchema = RJSFSchema>()
### rangeSpec\<S extends StrictRJSFSchema = RJSFSchema>()
Extracts the range spec information `{ step?: number, min?: number, max?: number }` that can be spread onto an HTML input from the range analog in the schema `{ multipleOf?: number, minimum?: number, maximum?: number }`.

#### Parameters
Expand Down Expand Up @@ -371,7 +372,7 @@ If either of those two sets are not the same, then the component should be reren
#### Returns
- True if boolean: the component should be re-rendered, false otherwise

### toConstant<S extends StrictRJSFSchema = RJSFSchema>()
### toConstant\<S extends StrictRJSFSchema = RJSFSchema>()
Returns the constant value from the schema when it is either a single value enum or has a const key.
Otherwise throws an error.

Expand Down Expand Up @@ -419,7 +420,7 @@ Returns the superset of `formData` that includes the given set updated to includ
#### Returns
- T: The resulting `formData` with all the defaults provided

### getDisplayLabel<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### getDisplayLabel<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Determines whether the combination of `schema` and `uiSchema` properties indicates that the label for the `schema` should be displayed in a UI.

#### Parameters
Expand All @@ -443,7 +444,7 @@ Given the `formData` and list of `options`, attempts to find the index of the op
#### Returns
- number: The index of the matched option or 0 if none is available

### isFilesArray<T = any, S extends StrictRJSFSchema = RJSFSchema,, F = any>()
### isFilesArray<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Checks to see if the `schema` and `uiSchema` combination represents an array of files

#### Parameters
Expand Down Expand Up @@ -533,7 +534,7 @@ Generates an `PathSchema` object for the `schema`, recursively

## Schema utils creation function

### createSchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F = any>()
### createSchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
Creates a `SchemaUtilsType` interface that is based around the given `validator` and `rootSchema` parameters.
The resulting interface implementation will forward the `validator` and `rootSchema` to all the wrapped APIs.

Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/components/Form.tsx
Expand Up @@ -5,6 +5,7 @@ import {
deepEquals,
ErrorSchema,
ErrorTransformer,
FormContextType,
GenericObjectType,
getTemplate,
getUiOptions,
Expand Down Expand Up @@ -37,7 +38,7 @@ import getDefaultRegistry from "../getDefaultRegistry";
export interface FormProps<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
> {
/** The JSON schema object for the form */
schema: S;
Expand Down Expand Up @@ -192,7 +193,7 @@ export interface FormProps<
export interface FormState<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
> {
/** The JSON schema object for the form */
schema: S;
Expand Down Expand Up @@ -226,7 +227,7 @@ export interface FormState<
export interface IChangeEvent<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
> extends Omit<
FormState<T, S, F>,
"schemaValidationErrors" | "schemaValidationErrorSchema"
Expand All @@ -239,7 +240,7 @@ export interface IChangeEvent<
export default class Form<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
> extends Component<FormProps<T, S, F>, FormState<T, S, F>> {
/** The ref used to hold the `form` element, this needs to be `any` because `tagName` or `_internalFormWrapper` can
* provide any possible type here
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/components/fields/ArrayField.tsx
Expand Up @@ -10,6 +10,7 @@ import {
ArrayFieldTemplateProps,
ErrorSchema,
FieldProps,
FormContextType,
IdSchema,
RJSFSchema,
StrictRJSFSchema,
Expand Down Expand Up @@ -73,7 +74,7 @@ function keyedToPlainFormData<T>(
class ArrayField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
> extends Component<FieldProps<T[], S, F>, ArrayFieldState<T>> {
/** Constructs an `ArrayField` from the `props`, generating the initial keyed data from the `formData`
*
Expand All @@ -98,7 +99,7 @@ class ArrayField<
static getDerivedStateFromProps<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
>(
nextProps: Readonly<FieldProps<T[], S, F>>,
prevState: Readonly<ArrayFieldState<T>>
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/components/fields/BooleanField.tsx
Expand Up @@ -4,6 +4,7 @@ import {
getUiOptions,
optionsList,
FieldProps,
FormContextType,
EnumOptionsType,
RJSFSchema,
StrictRJSFSchema,
Expand All @@ -18,7 +19,7 @@ import isObject from "lodash/isObject";
function BooleanField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
>(props: FieldProps<T, S, F>) {
const {
schema,
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/components/fields/MultiSchemaField.tsx
Expand Up @@ -5,6 +5,7 @@ import {
guessType,
deepEquals,
FieldProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";
Expand All @@ -24,7 +25,7 @@ type AnyOfFieldState = {
class AnyOfField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
> extends Component<FieldProps<T, S, F>, AnyOfFieldState> {
/** Constructs an `AnyOfField` with the given `props` to initialize the initially selected option in state
*
Expand Down
15 changes: 11 additions & 4 deletions packages/core/src/components/fields/NullField.tsx
@@ -1,14 +1,21 @@
import { useEffect } from "react";
import { FieldProps, RJSFSchema, StrictRJSFSchema } from "@rjsf/utils";
import {
FieldProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

/** The `NullField` component is used to render a field in the schema is null. It also ensures that the `formData` is
* also set to null if it has no value.
*
* @param props - The `FieldProps` for this template
*/
function NullField<T = any, S extends StrictRJSFSchema = RJSFSchema, F = any>(
props: FieldProps<T, S, F>
) {
function NullField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: FieldProps<T, S, F>) {
const { formData, onChange } = props;
useEffect(() => {
if (formData === undefined) {
Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/components/fields/NumberField.tsx
Expand Up @@ -2,6 +2,7 @@ import React, { useState, useCallback } from "react";
import {
asNumber,
FieldProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";
Expand Down Expand Up @@ -35,9 +36,11 @@ const trailingCharMatcher = /[0.]0*$/;
* value cached in the state. If it matches the cached value, the cached
* value is passed to the input instead of the formData value
*/
function NumberField<T = any, S extends StrictRJSFSchema = RJSFSchema, F = any>(
props: FieldProps<T, S, F>
) {
function NumberField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: FieldProps<T, S, F>) {
const { registry, onChange, formData, value: initialValue } = props;
const [lastValue, setLastValue] = useState(initialValue);
const { StringField } = registry.fields;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/components/fields/ObjectField.tsx
Expand Up @@ -5,6 +5,7 @@ import {
orderProperties,
ErrorSchema,
FieldProps,
FormContextType,
GenericObjectType,
IdSchema,
RJSFSchema,
Expand Down Expand Up @@ -35,7 +36,7 @@ type ObjectFieldState = {
class ObjectField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F = any
F extends FormContextType = any
> extends Component<FieldProps<T, S, F>, ObjectFieldState> {
/** Set up the initial state */
state = {
Expand Down

0 comments on commit c78a1c9

Please sign in to comment.