Skip to content

Commit

Permalink
fix: Add generic type support to the mui theme
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 `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 committed Dec 28, 2022
1 parent ff034df commit eae582f
Show file tree
Hide file tree
Showing 31 changed files with 597 additions and 271 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Expand Up @@ -15,6 +15,14 @@ it according to semantic versioning. For example, if your PR adds a breaking cha
should change the heading of the (upcoming) version to include a major version bump.
-->
# 5.0.0-beta-16

## @rjsf/core
- Fix passing of generic types to a few helper methods, partially fixing [#3072](https://github.com/rjsf-team/react-jsonschema-form/issues/3072)

## @rjsf/mui
- 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)

# 5.0.0-beta-15

## @rjsf/core
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
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
22 changes: 14 additions & 8 deletions packages/mui/src/AddButton/AddButton.tsx
@@ -1,17 +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,
...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, ...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 @@ -67,6 +80,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 @@ -85,6 +99,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;
}
23 changes: 18 additions & 5 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 Down Expand Up @@ -47,6 +62,4 @@ const CheckboxWidget = (props: WidgetProps) => {
label={label || ""}
/>
);
};

export default CheckboxWidget;
}
24 changes: 18 additions & 6 deletions packages/mui/src/CheckboxesWidget/CheckboxesWidget.tsx
Expand Up @@ -3,7 +3,12 @@ import Checkbox from "@mui/material/Checkbox";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormGroup from "@mui/material/FormGroup";
import FormLabel from "@mui/material/FormLabel";
import { WidgetProps } from "@rjsf/utils";
import {
FormContextType,
WidgetProps,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const selectValue = (value: any, selected: any, all: any) => {
const at = all.indexOf(value);
Expand All @@ -18,7 +23,16 @@ const deselectValue = (value: any, selected: any) => {
return selected.filter((v: any) => v !== value);
};

const CheckboxesWidget = ({
/** The `CheckboxesWidget` is a widget for rendering checkbox groups.
* It is typically used to represent an array of enums.
*
* @param props - The `WidgetProps` for this component
*/
export default function CheckboxesWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({
schema,
label,
id,
Expand All @@ -31,7 +45,7 @@ const CheckboxesWidget = ({
onChange,
onBlur,
onFocus,
}: WidgetProps) => {
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, inline } = options;

const _onChange =
Expand Down Expand Up @@ -88,6 +102,4 @@ const CheckboxesWidget = ({
</FormGroup>
</>
);
};

export default CheckboxesWidget;
}

0 comments on commit eae582f

Please sign in to comment.