Skip to content

Commit

Permalink
fix: Add generic type support to the mui theme (rjsf-team#3319)
Browse files Browse the repository at this point in the history
partially fix: rjsf-team#3072 by adding generic type support to the mui theme
- In `@rjsf/core` added a few missing generics to utility function calls in `CheckboxWidget`, `ObjectFieldTemplate`, `SubmitButton` and `SelectWidget`
- In `@rjsf/mui` added generic type support as follows:
  - Updated all of the template and widget components to add the `T = any`, `S extends StrictRJSFSchema = RJSFSchema` and `F extends FormContextType = any` generics on the function definitions, types and utility function calls
  - Added a `generateForm<T, S, F>()` function to `MuiForm.tsx`, exporting it and exporting the default `MuiForm` as a call to `generateForm()`
  - Added a `generateTemplates<T, S, F>()` function to the `Templates.tsx`, exporting it and exporting the default `Templates` as a call to `generateTemplates()`
  - Added a `generateTheme<T, S, F>()` function to the `Theme.tsx`, exporting it and exporting the default `Theme` as a call to `generateTheme()`
  - Added a `generateWidgets<T, S, F>()` function to the `Widgets.tsx`, exporting it and exporting the default `Widgets` as a call to `generateWidgets()`
  - Updated the main `index.ts` for the theme to also export the `generateXXX()` functions
- Fixed the number of new packages listes in the upgrade guide from 3 to 4
- Updated the `CHANGELOG.md` accordingly
  • Loading branch information
heath-freenome authored and shijistar committed Jun 8, 2023
1 parent 7532980 commit f47cd59
Show file tree
Hide file tree
Showing 33 changed files with 595 additions and 276 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -36,6 +36,7 @@ should change the heading of the (upcoming) version to include a major version b
- Also, passed `registry` into the `SubmitButton` inside of the `Form` as part of this fix
- Updated `ArrayField` to pass the new `totalItems` prop to the `ArrayFieldItemTemplate` instances, fixing [#3315](https://github.com/rjsf-team/react-jsonschema-form/issues/3315)
- Also refactored the near duplicate logic for `onAddClick` and `onAddIndexClick` into a new `_handleAddClick()` function, fixing [#3316](https://github.com/rjsf-team/react-jsonschema-form/issues/3316)
- Fix passing of generic types to a few helper methods, partially fixing [#3072](https://github.com/rjsf-team/react-jsonschema-form/issues/3072)

## @rjsf/fluent-ui
- Updated the usage of the `ButtonTemplates` to pass the new required `registry` prop, filtering it out in the actual implementations before spreading props, fixing - [#3314](https://github.com/rjsf-team/react-jsonschema-form/issues/3314)
Expand All @@ -48,6 +49,7 @@ should change the heading of the (upcoming) version to include a major version b
## @rjsf/mui
- Updated the usage of the `ButtonTemplates` to pass the new required `registry` prop, filtering it out in the actual implementations before spreading props, fixing - [#3314](https://github.com/rjsf-team/react-jsonschema-form/issues/3314)
- Updated the test for the `CheckboxWidget` validating that the `schema.title` is passed as the label, fixing [#3302](https://github.com/rjsf-team/react-jsonschema-form/issues/3302)
- Updated the theme to accept generic types, exporting `generateXXX` functions for `Form`, `Theme`, `Templates` and `Widgets` to support using the theme with user-specified type generics, partially fixing [#3072](https://github.com/rjsf-team/react-jsonschema-form/issues/3072)

## @rjsf/semantic-ui
- Updated the usage of the `ButtonTemplates` to pass the new required `registry` prop, filtering it out in the actual implementations before spreading props, fixing - [#3314](https://github.com/rjsf-team/react-jsonschema-form/issues/3314)
Expand Down
2 changes: 1 addition & 1 deletion docs/5.x upgrade guide.md
Expand Up @@ -24,7 +24,7 @@ Unfortunately, there is required work pending to properly support React 18, so u

### New packages

There are three new packages added in RJSF version 5:
There are four new packages added in RJSF version 5:

- `@rjsf/utils`: All of the [utility functions](https://react-jsonschema-form.readthedocs.io/en/stable/api-reference/utiltity-functions) previously imported from `@rjsf/core/utils` as well as the Typescript types for RJSF version 5.
- The following new utility functions were added: `createSchemaUtils()`, `getInputProps()`, `mergeValidationData()` and `processSelectValue()`
Expand Down
Expand Up @@ -18,7 +18,7 @@ export default function SubmitButton<
submitText,
norender,
props: submitButtonProps = {},
} = getSubmitButtonOptions(uiSchema);
} = getSubmitButtonOptions<T, S, F>(uiSchema);
if (norender) {
return null;
}
Expand Down
Expand Up @@ -73,7 +73,7 @@ export default function ObjectFieldTemplate<
/>
)}
{properties.map((prop: ObjectFieldTemplatePropertyType) => prop.content)}
{canExpand(schema, uiSchema, formData) && (
{canExpand<T, S, F>(schema, uiSchema, formData) && (
<AddButton
className="object-property-expand"
onClick={onAddClick(schema)}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/components/widgets/CheckboxWidget.tsx
Expand Up @@ -41,7 +41,7 @@ function CheckboxWidget<
// Because an unchecked checkbox will cause html5 validation to fail, only add
// the "required" attribute if the field value must be "true", due to the
// "const" or "enum" keywords
const required = schemaRequiresTrueValue(schema);
const required = schemaRequiresTrueValue<S>(schema);

const handleChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) =>
Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/components/widgets/SelectWidget.tsx
Expand Up @@ -50,23 +50,26 @@ function SelectWidget<
const handleFocus = useCallback(
(event: FocusEvent<HTMLSelectElement>) => {
const newValue = getValue(event, multiple);
return onFocus(id, processSelectValue(schema, newValue, options));
return onFocus(
id,
processSelectValue<T, S, F>(schema, newValue, options)
);
},
[onFocus, id, schema, multiple, options]
);

const handleBlur = useCallback(
(event: FocusEvent<HTMLSelectElement>) => {
const newValue = getValue(event, multiple);
return onBlur(id, processSelectValue(schema, newValue, options));
return onBlur(id, processSelectValue<T, S, F>(schema, newValue, options));
},
[onBlur, id, schema, multiple, options]
);

const handleChange = useCallback(
(event: ChangeEvent<HTMLSelectElement>) => {
const newValue = getValue(event, multiple);
return onChange(processSelectValue(schema, newValue, options));
return onChange(processSelectValue<T, S, F>(schema, newValue, options));
},
[onChange, schema, multiple, options]
);
Expand Down
23 changes: 14 additions & 9 deletions packages/mui/src/AddButton/AddButton.tsx
@@ -1,18 +1,23 @@
import React from "react";
import AddIcon from "@mui/icons-material/Add";
import IconButton from "@mui/material/IconButton";
import { IconButtonProps } from "@rjsf/utils";
import {
FormContextType,
IconButtonProps,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const AddButton: React.ComponentType<IconButtonProps> = ({
uiSchema,
registry,
...props
}) => {
/** The `AddButton` renders a button that represent the `Add` action on a form
*/
export default function AddButton<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({ uiSchema, registry, ...props }: IconButtonProps<T, S, F>) {
return (
<IconButton title="Add Item" {...props} color="primary">
<AddIcon />
</IconButton>
);
};

export default AddButton;
}
21 changes: 16 additions & 5 deletions packages/mui/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx
Expand Up @@ -2,9 +2,22 @@ import React, { CSSProperties } from "react";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import { ArrayFieldTemplateItemType } from "@rjsf/utils";
import {
ArrayFieldTemplateItemType,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
/** The `ArrayFieldItemTemplate` component is the template used to render an items of an array.
*
* @param props - The `ArrayFieldTemplateItemType` props for the component
*/
export default function ArrayFieldItemTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: ArrayFieldTemplateItemType<T, S, F>) {
const {
children,
disabled,
Expand Down Expand Up @@ -70,6 +83,4 @@ const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
)}
</Grid>
);
};

export default ArrayFieldItemTemplate;
}
54 changes: 33 additions & 21 deletions packages/mui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx
Expand Up @@ -3,13 +3,24 @@ import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import {
ArrayFieldTemplateItemType,
ArrayFieldTemplateProps,
getTemplate,
getUiOptions,
ArrayFieldTemplateProps,
ArrayFieldTemplateItemType,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
/** The `ArrayFieldTemplate` component is the template used to render all items in an array.
*
* @param props - The `ArrayFieldTemplateItemType` props for the component
*/
export default function ArrayFieldTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: ArrayFieldTemplateProps<T, S, F>) {
const {
canAdd,
disabled,
Expand All @@ -23,23 +34,24 @@ const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
schema,
title,
} = props;
const uiOptions = getUiOptions(uiSchema);
const ArrayFieldDescriptionTemplate =
getTemplate<"ArrayFieldDescriptionTemplate">(
"ArrayFieldDescriptionTemplate",
registry,
uiOptions
);
const ArrayFieldItemTemplate = getTemplate<"ArrayFieldItemTemplate">(
const uiOptions = getUiOptions<T, S, F>(uiSchema);
const ArrayFieldDescriptionTemplate = getTemplate<
"ArrayFieldDescriptionTemplate",
T,
S,
F
>("ArrayFieldDescriptionTemplate", registry, uiOptions);
const ArrayFieldItemTemplate = getTemplate<"ArrayFieldItemTemplate", T, S, F>(
"ArrayFieldItemTemplate",
registry,
uiOptions
);
const ArrayFieldTitleTemplate = getTemplate<"ArrayFieldTitleTemplate">(
const ArrayFieldTitleTemplate = getTemplate<
"ArrayFieldTitleTemplate",
registry,
uiOptions
);
T,
S,
F
>("ArrayFieldTitleTemplate", registry, uiOptions);
// Button templates are not overridden in the uiSchema
const {
ButtonTemplates: { AddButton },
Expand All @@ -64,9 +76,11 @@ const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
/>
<Grid container={true} key={`array-item-list-${idSchema.$id}`}>
{items &&
items.map(({ key, ...itemProps }: ArrayFieldTemplateItemType) => (
<ArrayFieldItemTemplate key={key} {...itemProps} />
))}
items.map(
({ key, ...itemProps }: ArrayFieldTemplateItemType<T, S, F>) => (
<ArrayFieldItemTemplate key={key} {...itemProps} />
)
)}
{canAdd && (
<Grid container justifyContent="flex-end">
<Grid item={true}>
Expand All @@ -86,6 +100,4 @@ const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
</Box>
</Paper>
);
};

export default ArrayFieldTemplate;
}
67 changes: 41 additions & 26 deletions packages/mui/src/BaseInputTemplate/BaseInputTemplate.tsx
@@ -1,29 +1,46 @@
import React from "react";
import TextField, { TextFieldProps } from "@mui/material/TextField";
import { getInputProps, WidgetProps } from "@rjsf/utils";
import {
getInputProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from "@rjsf/utils";

const BaseInputTemplate = ({
id,
placeholder,
required,
readonly,
disabled,
type,
label,
value,
onChange,
onBlur,
onFocus,
autofocus,
options,
schema,
uiSchema,
rawErrors = [],
formContext,
registry,
...textFieldProps
}: WidgetProps) => {
const inputProps = getInputProps(schema, type, options);
/** The `BaseInputTemplate` is the template to use to render the basic `<input>` component for the `core` theme.
* It is used as the template for rendering many of the <input> based widgets that differ by `type` and callbacks only.
* It can be customized/overridden for other themes or individual implementations as needed.
*
* @param props - The `WidgetProps` for this template
*/
export default function BaseInputTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: WidgetProps<T, S, F>) {
const {
id,
placeholder,
required,
readonly,
disabled,
type,
label,
value,
onChange,
onBlur,
onFocus,
autofocus,
options,
schema,
uiSchema,
rawErrors = [],
formContext,
registry,
...textFieldProps
} = props;
const inputProps = getInputProps<T, S, F>(schema, type, options);
// Now we need to pull out the step, min, max into an inner `inputProps` for material-ui
const { step, min, max, ...rest } = inputProps;
const otherProps = {
Expand Down Expand Up @@ -77,6 +94,4 @@ const BaseInputTemplate = ({
)}
</>
);
};

export default BaseInputTemplate;
}
25 changes: 19 additions & 6 deletions packages/mui/src/CheckboxWidget/CheckboxWidget.tsx
@@ -1,9 +1,24 @@
import React from "react";
import Checkbox from "@mui/material/Checkbox";
import FormControlLabel from "@mui/material/FormControlLabel";
import { schemaRequiresTrueValue, WidgetProps } from "@rjsf/utils";
import {
schemaRequiresTrueValue,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from "@rjsf/utils";

const CheckboxWidget = (props: WidgetProps) => {
/** The `CheckBoxWidget` is a widget for rendering boolean properties.
* It is typically used to represent a boolean.
*
* @param props - The `WidgetProps` for this component
*/
export default function CheckboxWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: WidgetProps<T, S, F>) {
const {
schema,
id,
Expand All @@ -19,7 +34,7 @@ const CheckboxWidget = (props: WidgetProps) => {
// Because an unchecked checkbox will cause html5 validation to fail, only add
// the "required" attribute if the field value must be "true", due to the
// "const" or "enum" keywords
const required = schemaRequiresTrueValue(schema);
const required = schemaRequiresTrueValue<S>(schema);

const _onChange = (_: any, checked: boolean) => onChange(checked);
const _onBlur = ({
Expand Down Expand Up @@ -47,6 +62,4 @@ const CheckboxWidget = (props: WidgetProps) => {
label={label || ""}
/>
);
};

export default CheckboxWidget;
}

0 comments on commit f47cd59

Please sign in to comment.