Skip to content

Commit

Permalink
fix: Add generic type support to the muterial-ui theme (rjsf-team#3329)
Browse files Browse the repository at this point in the history
partially fix: rjsf-team#3072 by adding generic type support to the `material-ui` theme as follows:

- In `@rjsf/material-ui` 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
- Updated the `CHANGELOG.md` accordingly
  • Loading branch information
heath-freenome authored and shijistar committed Jun 8, 2023
1 parent 14d07ba commit 3029b09
Show file tree
Hide file tree
Showing 28 changed files with 583 additions and 268 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -45,6 +45,7 @@ should change the heading of the (upcoming) version to include a major version b
## @rjsf/material-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)
- 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/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)
Expand Down
23 changes: 14 additions & 9 deletions packages/material-ui/src/AddButton/AddButton.tsx
@@ -1,18 +1,23 @@
import React from "react";
import AddIcon from "@material-ui/icons/Add";
import IconButton from "@material-ui/core/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;
}
Expand Up @@ -2,9 +2,22 @@ import React, { CSSProperties } from "react";
import Box from "@material-ui/core/Box";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/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 @@ -71,6 +84,4 @@ const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
)}
</Grid>
);
};

export default ArrayFieldItemTemplate;
}
52 changes: 32 additions & 20 deletions packages/material-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx
Expand Up @@ -3,13 +3,24 @@ import Box from "@material-ui/core/Box";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/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 @@ -24,22 +35,23 @@ const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
title,
} = props;
const uiOptions = getUiOptions(uiSchema);
const ArrayFieldDescriptionTemplate =
getTemplate<"ArrayFieldDescriptionTemplate">(
"ArrayFieldDescriptionTemplate",
registry,
uiOptions
);
const ArrayFieldItemTemplate = getTemplate<"ArrayFieldItemTemplate">(
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/material-ui/src/BaseInputTemplate/BaseInputTemplate.tsx
@@ -1,29 +1,46 @@
import React from "react";
import TextField, { TextFieldProps } from "@material-ui/core/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/material-ui/src/CheckboxWidget/CheckboxWidget.tsx
@@ -1,9 +1,24 @@
import React from "react";
import Checkbox from "@material-ui/core/Checkbox";
import FormControlLabel from "@material-ui/core/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;
}
24 changes: 18 additions & 6 deletions packages/material-ui/src/CheckboxesWidget/CheckboxesWidget.tsx
Expand Up @@ -3,7 +3,12 @@ import Checkbox from "@material-ui/core/Checkbox";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormGroup from "@material-ui/core/FormGroup";
import FormLabel from "@material-ui/core/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;
}
27 changes: 21 additions & 6 deletions packages/material-ui/src/DateTimeWidget/DateTimeWidget.tsx
@@ -1,9 +1,26 @@
import React from "react";
import { getTemplate, localToUTC, utcToLocal, WidgetProps } from "@rjsf/utils";
import {
getTemplate,
localToUTC,
utcToLocal,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from "@rjsf/utils";

const DateTimeWidget = (props: WidgetProps) => {
/** The `DateTimeWidget` component uses the `BaseInputTemplate` changing the type to `datetime-local` and transforms
* the value to/from utc using the appropriate utility functions.
*
* @param props - The `WidgetProps` for this component
*/
export default function DateTimeWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: WidgetProps<T, S, F>) {
const { options, registry } = props;
const BaseInputTemplate = getTemplate<"BaseInputTemplate">(
const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, S, F>(
"BaseInputTemplate",
registry,
options
Expand All @@ -24,6 +41,4 @@ const DateTimeWidget = (props: WidgetProps) => {
onChange={onChange}
/>
);
};

export default DateTimeWidget;
}

0 comments on commit 3029b09

Please sign in to comment.