Skip to content

Commit b63ec99

Browse files
fix: Partially fix #3189 (#3200)
* fix: Partially fix #3189 - Updated `@rjsf/utils` to make the `schema` and `rootSchema` props use a new generic type `S` - Added the new `StrictRJSFSchema` type as the alias to `JSON7Schema` changing `RJSFSchema` to be `StrictRJSFSchema & GenericObjectType` - Deleted the `RJSFSchemaDefinition` type in favor of accessing it indirectly via the `S["<prop-with-definition>"]` syntax - Added the new generic `S extends StrictRJSFSchema = RJSFSchema` to all types that directly or indirectly used `RJSFSchema` after the `T = any` type - Updated `SchemaUtilsType` to add the `F = any` generic to the whole interface, removing it from the definition of the two functions that need it - Updated all functions that used `RJSFSchema` to take the new generic, replacing `RJSFSchema` with `S` - Added missing generics where needed - Updated `@rjsf/core` to insert the `S extends StrictRJSFSchema = RJSFSchema` to every component that needed it, after the `T = any` generic - Updated the `index.ts` for the `ButtonTemplates`, `field`, `templates` and `widgets` to make them functions that take the `T`, `S` and `F` generics - Updated `getDefaultRegistry()` and `templates()` to call the appropriate functions - Replaced all uses of `RJSFSchema` and `RJSFSchemaDefinition` with `S` and `S["<prop-with-definition>"]` - Added missing generics where needed - Fixed a few type casts due the to the change in the `RJSFSchema` type - Updated `@rjsf/validator-ajv6` to fix a few type casts due to the change in the `RJSFSchema` type - Updated `@rjsf/validator-ajv8` to add the `S extends StrictRJSFSchema = RJSFSchema` generic to the `customizeValidator()` function and the `AJV8Validator` class - Replaced all uses of `RJSFSchema` and `RJSFSchemaDefinition` with `S` and `S["<prop-with-definition>"]` - Changed `RJSFSchema` to `S` where applicable - Fixed a few type casts due the to the change in the `RJSFSchema` type * Update packages/validator-ajv8/src/validator.ts Fix comment Co-authored-by: Nick Grosenbacher <[email protected]> Co-authored-by: Nick Grosenbacher <[email protected]>
1 parent 27a8c70 commit b63ec99

File tree

91 files changed

+1488
-897
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+1488
-897
lines changed

CHANGELOG.md

+18
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ should change the heading of the (upcoming) version to include a major version b
2020
## @rjsf/chakra-ui
2121
- Automatically close single-choice Select widget on selection
2222

23+
## @rjsf/core
24+
- Added the new generic, `S extends StrictRJSFSchema = RJSFSchema`, for `schema`/`rootSchema` to every component that needed it.
25+
26+
## @rjsf/utils
27+
- Beta-only potentially BREAKING CHANGE: Changed all types that directly or indirectly defined `schema`/`rootSchema` to add the generic `S extends StrictRJSFSchema = RJSFSchema` and use `S` as the type for them.
28+
- `StrictRJSFSchema` was added as the alias to `JSON7Schema` and `RJSFSchema` was modified to be `StrictRJSFSchema & GenericObjectType`
29+
- 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
30+
- This provides future support for the newer draft versions of the schema
31+
32+
## @rjsf/validator-ajv6
33+
- Fixed a few type casts given the new expanded definition of the `RJSFSchema` type change
34+
35+
## @rjsf/validator-ajv8
36+
- Updated the typing to add the new `S extends StrictRJSFSchema = RJSFSchema` generic and fixed up type casts
37+
38+
## Dev / docs / playground
39+
- Updated the `5.x upgrade guide` to document the new `StrictRJSFSchema` and `S` generic
40+
2341
# 5.0.0-beta.11
2442

2543
## @rjsf/antd

docs/5.x upgrade guide.md

+15-15
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ All the rest of the types for RJSF are now exported from the new `@rjsf/utils` p
4242
NOTE: The types in `@rjsf/utils` have been improved significantly from those in version 4.
4343
Some of the most notable changes are:
4444

45-
- `RJSFSchema` has replaced the use of `JSON7Schema` for future compatibility reasons.
46-
- Currently `RJSFSchema` is simply an alias to `JSON7Schema` so this change is purely a naming one.
47-
- It is highly recommended to update your use of `JSON7Schema` with `RJSFSchema` so that when the RJSF begins supporting a newer JSON Schema version out-of-the-box, your code won't be affected.
48-
- `RJSFSchemaDefinition` has replaced the use of `JSONSchema7Definition` for the same reasons.
4945
- The use of the generic `T` (defaulting to `any`) for the `formData` type has been expanded to cover all type hierarchies that use `formData`.
46+
- `StrictRJSFSchema` and `RJSFSchema` have replaced the use of `JSON7Schema` for future compatibility reasons.
47+
- `RJSFSchema` is `StrictRJSFSchema` joined with the `GenericObjectType` (i.e. `{ [key: string]: any }`) to allow for additional syntax related to newer draft versions
48+
- All definitions of `schema` and `rootSchema` elements have been replaced with a generic that is defined as `S extends StrictRJSFSchema = RJSFSchema`
49+
- 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`
5050
- 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.
5151
- The new `CustomValidator`, `ErrorTransformer`, `ValidationData`, `ValidatorType` and `SchemaUtilsType` types were added to support the decoupling of the validation implementation.
5252
- 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`.
@@ -362,21 +362,21 @@ For example, given a schema such as:
362362
{
363363
"properties": {
364364
"foo": {
365-
"type": "string",
366-
},
367-
},
365+
"type": "string"
366+
}
367+
}
368368
},
369369
{
370370
"properties": {
371371
"bar": {
372-
"type": "string",
373-
},
374-
},
375-
},
376-
],
377-
},
378-
},
379-
},
372+
"type": "string"
373+
}
374+
}
375+
}
376+
]
377+
}
378+
}
379+
}
380380
}
381381
```
382382

packages/core/src/components/Form.tsx

+58-41
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
IdSchema,
88
PathSchema,
99
RJSFSchema,
10+
StrictRJSFSchema,
1011
RJSFValidationError,
1112
Registry,
1213
RegistryWidgetsType,
@@ -33,15 +34,19 @@ import _isEmpty from "lodash/isEmpty";
3334
import getDefaultRegistry from "../getDefaultRegistry";
3435

3536
/** The properties that are passed to the `Form` */
36-
export interface FormProps<T = any, F = any> {
37+
export interface FormProps<
38+
T = any,
39+
S extends StrictRJSFSchema = RJSFSchema,
40+
F = any
41+
> {
3742
/** The JSON schema object for the form */
38-
schema: RJSFSchema;
43+
schema: S;
3944
/** An implementation of the `ValidatorType` interface that is needed for form validation to work */
40-
validator: ValidatorType<T>;
45+
validator: ValidatorType<T, S>;
4146
/** The optional children for the form, if provided, it will replace the default `SubmitButton` */
4247
children?: React.ReactNode;
4348
/** The uiSchema for the form */
44-
uiSchema?: UiSchema<T, F>;
49+
uiSchema?: UiSchema<T, S, F>;
4550
/** The data for the form, used to prefill a form with existing data */
4651
formData?: T;
4752
// Form presentation and behavior modifiers
@@ -71,19 +76,19 @@ export interface FormProps<T = any, F = any> {
7176
readonly?: boolean;
7277
// Form registry
7378
/** The dictionary of registered fields in the form */
74-
fields?: RegistryFieldsType<T, F>;
79+
fields?: RegistryFieldsType<T, S, F>;
7580
/** The dictionary of registered templates in the form; Partial allows a subset to be provided beyond the defaults */
76-
templates?: Partial<Omit<TemplatesType<T, F>, "ButtonTemplates">> & {
77-
ButtonTemplates?: Partial<TemplatesType<T, F>["ButtonTemplates"]>;
81+
templates?: Partial<Omit<TemplatesType<T, S, F>, "ButtonTemplates">> & {
82+
ButtonTemplates?: Partial<TemplatesType<T, S, F>["ButtonTemplates"]>;
7883
};
7984
/** The dictionary of registered widgets in the form */
80-
widgets?: RegistryWidgetsType<T, F>;
85+
widgets?: RegistryWidgetsType<T, S, F>;
8186
// Callbacks
8287
/** If you plan on being notified every time the form data are updated, you can pass an `onChange` handler, which will
8388
* receive the same args as `onSubmit` any time a value is updated in the form. Can also return the `id` of the field
8489
* that caused the change
8590
*/
86-
onChange?: (data: IChangeEvent<T, F>, id?: string) => void;
91+
onChange?: (data: IChangeEvent<T, S, F>, id?: string) => void;
8792
/** To react when submitted form data are invalid, pass an `onError` handler. It will be passed the list of
8893
* encountered errors
8994
*/
@@ -92,7 +97,7 @@ export interface FormProps<T = any, F = any> {
9297
* and its data are valid. It will be passed a result object having a `formData` attribute, which is the valid form
9398
* data you're usually after. The original event will also be passed as a second parameter
9499
*/
95-
onSubmit?: (data: IChangeEvent<T, F>, event: React.FormEvent<any>) => void;
100+
onSubmit?: (data: IChangeEvent<T, S, F>, event: React.FormEvent<any>) => void;
96101
/** Sometimes you may want to trigger events or modify external state when a field has been touched, so you can pass
97102
* an `onBlur` handler, which will receive the id of the input that was blurred and the field value
98103
*/
@@ -184,17 +189,21 @@ export interface FormProps<T = any, F = any> {
184189
}
185190

186191
/** The data that is contained within the state for the `Form` */
187-
export interface FormState<T = any, F = any> {
192+
export interface FormState<
193+
T = any,
194+
S extends StrictRJSFSchema = RJSFSchema,
195+
F = any
196+
> {
188197
/** The JSON schema object for the form */
189-
schema: RJSFSchema;
198+
schema: S;
190199
/** The uiSchema for the form */
191-
uiSchema: UiSchema<T, F>;
200+
uiSchema: UiSchema<T, S, F>;
192201
/** The `IdSchema` for the form, computed from the `schema`, the `rootFieldId`, the `formData` and the `idPrefix` and
193202
* `idSeparator` props.
194203
*/
195204
idSchema: IdSchema<T>;
196205
/** The schemaUtils implementation used by the `Form`, created from the `validator` and the `schema` */
197-
schemaUtils: SchemaUtilsType<T>;
206+
schemaUtils: SchemaUtilsType<T, S, F>;
198207
/** The current data for the form, computed from the `formData` prop and the changes made by the user */
199208
formData: T;
200209
/** Flag indicating whether the form is in edit mode, true when `formData` is passed to the form, otherwise false */
@@ -214,20 +223,24 @@ export interface FormState<T = any, F = any> {
214223
/** The event data passed when changes have been made to the form, includes everything from the `FormState` except
215224
* the schema validation errors. An additional `status` is added when returned from `onSubmit`
216225
*/
217-
export interface IChangeEvent<T = any, F = any>
218-
extends Omit<
219-
FormState<T, F>,
226+
export interface IChangeEvent<
227+
T = any,
228+
S extends StrictRJSFSchema = RJSFSchema,
229+
F = any
230+
> extends Omit<
231+
FormState<T, S, F>,
220232
"schemaValidationErrors" | "schemaValidationErrorSchema"
221233
> {
222234
/** The status of the form when submitted */
223235
status?: "submitted";
224236
}
225237

226238
/** The `Form` component renders the outer form and all the fields defined in the `schema` */
227-
export default class Form<T = any, F = any> extends Component<
228-
FormProps<T, F>,
229-
FormState<T, F>
230-
> {
239+
export default class Form<
240+
T = any,
241+
S extends StrictRJSFSchema = RJSFSchema,
242+
F = any
243+
> extends Component<FormProps<T, S, F>, FormState<T, S, F>> {
231244
/** The ref used to hold the `form` element, this needs to be `any` because `tagName` or `_internalFormWrapper` can
232245
* provide any possible type here
233246
*/
@@ -239,7 +252,7 @@ export default class Form<T = any, F = any> extends Component<
239252
*
240253
* @param props - The initial props for the `Form`
241254
*/
242-
constructor(props: FormProps<T, F>) {
255+
constructor(props: FormProps<T, S, F>) {
243256
super(props);
244257

245258
if (!props.validator) {
@@ -262,7 +275,7 @@ export default class Form<T = any, F = any> extends Component<
262275
*
263276
* @param nextProps - The new set of props about to be applied to the `Form`
264277
*/
265-
UNSAFE_componentWillReceiveProps(nextProps: FormProps<T, F>) {
278+
UNSAFE_componentWillReceiveProps(nextProps: FormProps<T, S, F>) {
266279
const nextState = this.getStateFromProps(nextProps, nextProps.formData);
267280
if (
268281
!deepEquals(nextState.formData, nextProps.formData) &&
@@ -283,24 +296,24 @@ export default class Form<T = any, F = any> extends Component<
283296
* @returns - The new state for the `Form`
284297
*/
285298
getStateFromProps(
286-
props: FormProps<T, F>,
299+
props: FormProps<T, S, F>,
287300
inputFormData?: T
288-
): FormState<T, F> {
289-
const state: FormState<T, F> = this.state || {};
301+
): FormState<T, S, F> {
302+
const state: FormState<T, S, F> = this.state || {};
290303
const schema = "schema" in props ? props.schema : this.props.schema;
291-
const uiSchema: UiSchema<T, F> =
304+
const uiSchema: UiSchema<T, S, F> =
292305
("uiSchema" in props ? props.uiSchema! : this.props.uiSchema!) || {};
293306
const edit = typeof inputFormData !== "undefined";
294307
const liveValidate =
295308
"liveValidate" in props ? props.liveValidate : this.props.liveValidate;
296309
const mustValidate = edit && !props.noValidate && liveValidate;
297310
const rootSchema = schema;
298-
let schemaUtils: SchemaUtilsType<T> = state.schemaUtils;
311+
let schemaUtils: SchemaUtilsType<T, S> = state.schemaUtils;
299312
if (
300313
!schemaUtils ||
301314
schemaUtils.doesSchemaUtilsDiffer(props.validator, rootSchema)
302315
) {
303-
schemaUtils = createSchemaUtils<T>(props.validator, rootSchema);
316+
schemaUtils = createSchemaUtils<T, S, F>(props.validator, rootSchema);
304317
}
305318
const formData: T = schemaUtils.getDefaultFormState(
306319
schema,
@@ -355,7 +368,7 @@ export default class Form<T = any, F = any> extends Component<
355368
props.idPrefix,
356369
props.idSeparator
357370
);
358-
const nextState: FormState<T, F> = {
371+
const nextState: FormState<T, S, F> = {
359372
schemaUtils,
360373
schema,
361374
uiSchema,
@@ -377,8 +390,8 @@ export default class Form<T = any, F = any> extends Component<
377390
* @returns - True if the component should be updated, false otherwise
378391
*/
379392
shouldComponentUpdate(
380-
nextProps: FormProps<T, F>,
381-
nextState: FormState<T, F>
393+
nextProps: FormProps<T, S, F>,
394+
nextState: FormState<T, S, F>
382395
): boolean {
383396
return shouldRender(this, nextProps, nextState);
384397
}
@@ -393,7 +406,7 @@ export default class Form<T = any, F = any> extends Component<
393406
validate(
394407
formData: T,
395408
schema = this.props.schema,
396-
altSchemaUtils?: SchemaUtilsType<T>
409+
altSchemaUtils?: SchemaUtilsType<T, S>
397410
): ValidationData<T> {
398411
const schemaUtils = altSchemaUtils
399412
? altSchemaUtils
@@ -411,11 +424,11 @@ export default class Form<T = any, F = any> extends Component<
411424
}
412425

413426
/** Renders any errors contained in the `state` in using the `ErrorList`, if not disabled by `showErrorList`. */
414-
renderErrors(registry: Registry<T, F>) {
427+
renderErrors(registry: Registry<T, S, F>) {
415428
const { errors, errorSchema, schema, uiSchema } = this.state;
416429
const { showErrorList, formContext } = this.props;
417-
const options = getUiOptions<T, F>(uiSchema);
418-
const ErrorListTemplate = getTemplate<"ErrorListTemplate", T, F>(
430+
const options = getUiOptions<T, S, F>(uiSchema);
431+
const ErrorListTemplate = getTemplate<"ErrorListTemplate", T, S, F>(
419432
"ErrorListTemplate",
420433
registry,
421434
options
@@ -522,7 +535,7 @@ export default class Form<T = any, F = any> extends Component<
522535
}
523536

524537
const mustValidate = !noValidate && liveValidate;
525-
let state: Partial<FormState<T, F>> = { formData, schema };
538+
let state: Partial<FormState<T, S, F>> = { formData, schema };
526539
let newFormData = formData;
527540

528541
if (omitExtraData === true && liveOmit === true) {
@@ -573,7 +586,7 @@ export default class Form<T = any, F = any> extends Component<
573586
};
574587
}
575588
this.setState(
576-
state as FormState<T, F>,
589+
state as FormState<T, S, F>,
577590
() => onChange && onChange({ ...this.state, ...state }, id)
578591
);
579592
};
@@ -664,9 +677,13 @@ export default class Form<T = any, F = any> extends Component<
664677
};
665678

666679
/** Returns the registry for the form */
667-
getRegistry(): Registry<T, F> {
680+
getRegistry(): Registry<T, S, F> {
668681
const { schemaUtils } = this.state;
669-
const { fields, templates, widgets, formContext } = getDefaultRegistry();
682+
const { fields, templates, widgets, formContext } = getDefaultRegistry<
683+
T,
684+
S,
685+
F
686+
>();
670687
return {
671688
fields: { ...fields, ...this.props.fields },
672689
templates: {
@@ -769,7 +786,7 @@ export default class Form<T = any, F = any> extends Component<
769786
const { SchemaField: _SchemaField } = registry.fields;
770787
const { SubmitButton } = registry.templates.ButtonTemplates;
771788
// The `semantic-ui` and `material-ui` themes have `_internalFormWrapper`s that take an `as` prop that is the
772-
// PropTypes.elementType to use for the inner tag so we'll need to pass `tagName` along if it is provided.
789+
// PropTypes.elementType to use for the inner tag, so we'll need to pass `tagName` along if it is provided.
773790
// NOTE, the `as` prop is native to `semantic-ui` and is emulated in the `material-ui` theme
774791
const as = _internalFormWrapper ? tagName : undefined;
775792
const FormTag = _internalFormWrapper || tagName || "form";

0 commit comments

Comments
 (0)