Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add generic type support to the material-ui theme #3329

Merged
merged 2 commits into from Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
}