diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4f19f344..9534259aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ should change the heading of the (upcoming) version to include a major version b - Updated `CheckboxWidget` to get the `required` state of the checkbox from the `schemaRequiresTrueValue()` utility function rather than the `required` prop, fixing [#3317](https://github.com/rjsf-team/react-jsonschema-form/issues/3317) - Also fixed the `CheckboxWidget` missing label issue [#3302](https://github.com/rjsf-team/react-jsonschema-form/issues/3302) - 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/utils - Updated the `SubmitButtonProps` and `IconButtonProps` to add required `registry` prop, fixing - [#3314](https://github.com/rjsf-team/react-jsonschema-form/issues/3314) diff --git a/packages/semantic-ui/src/AddButton/AddButton.tsx b/packages/semantic-ui/src/AddButton/AddButton.tsx index fc025ba2b4..8f5e63a0ce 100644 --- a/packages/semantic-ui/src/AddButton/AddButton.tsx +++ b/packages/semantic-ui/src/AddButton/AddButton.tsx @@ -1,8 +1,19 @@ import React from "react"; -import { IconButtonProps } from "@rjsf/utils"; import { Button, Icon, ButtonProps } from "semantic-ui-react"; +import { + FormContextType, + IconButtonProps, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; -function AddButton({ uiSchema, registry, color, ...props }: IconButtonProps) { +/** 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, color, ...props }: IconButtonProps) { return ( ); -}; +} diff --git a/packages/semantic-ui/src/Templates/Templates.ts b/packages/semantic-ui/src/Templates/Templates.ts index 668826bc57..357980042a 100644 --- a/packages/semantic-ui/src/Templates/Templates.ts +++ b/packages/semantic-ui/src/Templates/Templates.ts @@ -1,4 +1,9 @@ -import { TemplatesType } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + TemplatesType, +} from "@rjsf/utils"; import AddButton from "../AddButton"; import ArrayFieldItemTemplate from "../ArrayFieldItemTemplate"; @@ -15,25 +20,31 @@ import SubmitButton from "../SubmitButton"; import TitleField from "../TitleField"; import WrapIfAdditionalTemplate from "../WrapIfAdditionalTemplate"; -const Templates: Partial = { - ArrayFieldItemTemplate, - ArrayFieldTemplate, - BaseInputTemplate, - ButtonTemplates: { - AddButton, - MoveDownButton, - MoveUpButton, - RemoveButton, - SubmitButton, - }, - DescriptionFieldTemplate: DescriptionField, - ErrorListTemplate: ErrorList, - FieldErrorTemplate, - FieldHelpTemplate, - FieldTemplate, - ObjectFieldTemplate, - TitleFieldTemplate: TitleField, - WrapIfAdditionalTemplate, -}; +export function generateTemplates< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): Partial> { + return { + ArrayFieldItemTemplate, + ArrayFieldTemplate, + BaseInputTemplate, + ButtonTemplates: { + AddButton, + MoveDownButton, + MoveUpButton, + RemoveButton, + SubmitButton, + }, + DescriptionFieldTemplate: DescriptionField, + ErrorListTemplate: ErrorList, + FieldErrorTemplate, + FieldHelpTemplate, + FieldTemplate, + ObjectFieldTemplate, + TitleFieldTemplate: TitleField, + WrapIfAdditionalTemplate, + }; +} -export default Templates; +export default generateTemplates(); diff --git a/packages/semantic-ui/src/TextareaWidget/TextareaWidget.tsx b/packages/semantic-ui/src/TextareaWidget/TextareaWidget.tsx index a5aba27039..9774897c45 100644 --- a/packages/semantic-ui/src/TextareaWidget/TextareaWidget.tsx +++ b/packages/semantic-ui/src/TextareaWidget/TextareaWidget.tsx @@ -1,9 +1,22 @@ import React from "react"; -import { WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import { Form } from "semantic-ui-react"; import { getSemanticProps } from "../util"; -function TextareaWidget(props: WidgetProps) { +/** The `TextareaWidget` is a widget for rendering input fields as textarea. + * + * @param props - The `WidgetProps` for this component + */ +export default function TextareaWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WidgetProps) { const { id, placeholder, @@ -23,7 +36,7 @@ function TextareaWidget(props: WidgetProps) { registry, rawErrors = [], } = props; - const semanticProps = getSemanticProps({ + const semanticProps = getSemanticProps({ formContext, options, defaultSchemaProps: { inverted: false }, @@ -57,4 +70,3 @@ function TextareaWidget(props: WidgetProps) { /> ); } -export default TextareaWidget; diff --git a/packages/semantic-ui/src/Theme/Theme.ts b/packages/semantic-ui/src/Theme/Theme.ts index 26316dc0ce..1b23dc3802 100644 --- a/packages/semantic-ui/src/Theme/Theme.ts +++ b/packages/semantic-ui/src/Theme/Theme.ts @@ -1,13 +1,20 @@ +import { FormContextType, RJSFSchema, StrictRJSFSchema } from "@rjsf/utils"; import { ThemeProps } from "@rjsf/core"; import { Form as SuiForm } from "semantic-ui-react"; -import Templates from "../Templates"; -import Widgets from "../Widgets"; +import { generateTemplates } from "../Templates"; +import { generateWidgets } from "../Widgets"; -const Theme: ThemeProps = { - templates: Templates, - widgets: Widgets, - _internalFormWrapper: SuiForm, -}; +export function generateTheme< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): ThemeProps { + return { + templates: generateTemplates(), + widgets: generateWidgets(), + _internalFormWrapper: SuiForm, + }; +} -export default Theme; +export default generateTheme(); diff --git a/packages/semantic-ui/src/TitleField/TitleField.tsx b/packages/semantic-ui/src/TitleField/TitleField.tsx index a633966ffe..0e1de44671 100644 --- a/packages/semantic-ui/src/TitleField/TitleField.tsx +++ b/packages/semantic-ui/src/TitleField/TitleField.tsx @@ -1,5 +1,10 @@ import React from "react"; -import { TitleFieldProps } from "@rjsf/utils"; +import { + FormContextType, + TitleFieldProps, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; import { Header } from "semantic-ui-react"; import { getSemanticProps } from "../util"; @@ -9,8 +14,16 @@ const DEFAULT_OPTIONS = { dividing: true, }; -function TitleField({ id, title, uiSchema }: TitleFieldProps) { - const semanticProps = getSemanticProps({ +/** The `TitleField` is the template to use to render the title of a field + * + * @param props - The `TitleFieldProps` for this component + */ +export default function TitleField< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ id, title, uiSchema }: TitleFieldProps) { + const semanticProps = getSemanticProps({ uiSchema, defaultSchemaProps: DEFAULT_OPTIONS, }); @@ -23,5 +36,3 @@ function TitleField({ id, title, uiSchema }: TitleFieldProps) { ); } - -export default TitleField; diff --git a/packages/semantic-ui/src/Widgets/Widgets.tsx b/packages/semantic-ui/src/Widgets/Widgets.tsx index f861d2cfd6..6fe835ead8 100644 --- a/packages/semantic-ui/src/Widgets/Widgets.tsx +++ b/packages/semantic-ui/src/Widgets/Widgets.tsx @@ -1,3 +1,10 @@ +import { + FormContextType, + RegistryWidgetsType, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; + import CheckboxWidget from "../CheckboxWidget/CheckboxWidget"; import CheckboxesWidget from "../CheckboxesWidget/CheckboxesWidget"; import RadioWidget from "../RadioWidget/RadioWidget"; @@ -5,11 +12,19 @@ import RangeWidget from "../RangeWidget/RangeWidget"; import SelectWidget from "../SelectWidget/SelectWidget"; import TextareaWidget from "../TextareaWidget/TextareaWidget"; -export default { - CheckboxWidget, - CheckboxesWidget, - RadioWidget, - RangeWidget, - SelectWidget, - TextareaWidget, -}; +export function generateWidgets< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): RegistryWidgetsType { + return { + CheckboxWidget, + CheckboxesWidget, + RadioWidget, + RangeWidget, + SelectWidget, + TextareaWidget, + }; +} + +export default generateWidgets(); diff --git a/packages/semantic-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx b/packages/semantic-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx index 32d3767204..c7b4fe4588 100644 --- a/packages/semantic-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx +++ b/packages/semantic-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx @@ -1,24 +1,37 @@ import React from "react"; import { ADDITIONAL_PROPERTY_FLAG, + FormContextType, + RJSFSchema, + StrictRJSFSchema, WrapIfAdditionalTemplateProps, } from "@rjsf/utils"; import { Form, Grid } from "semantic-ui-react"; -const WrapIfAdditionalTemplate = ({ - children, - classNames, - disabled, - id, - label, - onDropPropertyClick, - onKeyChange, - readonly, - required, - schema, - uiSchema, - registry, -}: WrapIfAdditionalTemplateProps) => { +/** The `WrapIfAdditional` component is used by the `FieldTemplate` to rename, or remove properties that are + * part of an `additionalProperties` part of a schema. + * + * @param props - The `WrapIfAdditionalProps` for this component + */ +export default function WrapIfAdditionalTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WrapIfAdditionalTemplateProps) { + const { + children, + classNames, + disabled, + id, + label, + onDropPropertyClick, + onKeyChange, + readonly, + required, + schema, + uiSchema, + registry, + } = props; // Button templates are not overridden in the uiSchema const { RemoveButton } = registry.templates.ButtonTemplates; const { readonlyAsDisabled = true, wrapperStyle } = registry.formContext; @@ -73,6 +86,4 @@ const WrapIfAdditionalTemplate = ({ ); -}; - -export default WrapIfAdditionalTemplate; +} diff --git a/packages/semantic-ui/src/index.ts b/packages/semantic-ui/src/index.ts index 166be2e02b..c089b7f177 100644 --- a/packages/semantic-ui/src/index.ts +++ b/packages/semantic-ui/src/index.ts @@ -1,8 +1,8 @@ import SemanticUIForm from "./SemanticUIForm/SemanticUIForm"; -export { default as Templates } from "./Templates"; -export { default as Form } from "./SemanticUIForm"; -export { default as Theme } from "./Theme"; -export { default as Widgets } from "./Widgets"; +export { default as Templates, generateTemplates } from "./Templates"; +export { default as Form, generateForm } from "./SemanticUIForm"; +export { default as Theme, generateTheme } from "./Theme"; +export { default as Widgets, generateWidgets } from "./Widgets"; export default SemanticUIForm; diff --git a/packages/semantic-ui/src/util.tsx b/packages/semantic-ui/src/util.tsx index fe9d692b78..3d1908fa55 100644 --- a/packages/semantic-ui/src/util.tsx +++ b/packages/semantic-ui/src/util.tsx @@ -3,21 +3,32 @@ import { UiSchema, GenericObjectType, getUiOptions, + FormContextType, + RJSFSchema, + StrictRJSFSchema, UIOptionsType, } from "@rjsf/utils"; -export type SemanticPropsTypes = { - formContext?: GenericObjectType; - uiSchema?: UiSchema; - options?: UIOptionsType; +export type SemanticPropsTypes< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +> = { + formContext?: F; + uiSchema?: UiSchema; + options?: UIOptionsType; defaultSchemaProps?: GenericObjectType; defaultContextProps?: GenericObjectType; }; -export type SemanticErrorPropsType = { - formContext?: GenericObjectType; - uiSchema?: UiSchema; - options?: UIOptionsType; +export type SemanticErrorPropsType< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +> = { + formContext?: F; + uiSchema?: UiSchema; + options?: UIOptionsType; defaultProps?: GenericObjectType; }; @@ -37,15 +48,19 @@ export type WrapProps = GenericObjectType & { * @param {Object?} params.defaultContextProps * @returns {any} */ -export function getSemanticProps({ - formContext = {}, +export function getSemanticProps< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ + formContext = {} as F, uiSchema = {}, options = {}, defaultSchemaProps = { fluid: true, inverted: false }, defaultContextProps = {}, -}: SemanticPropsTypes) { +}: SemanticPropsTypes) { const formContextProps = formContext.semantic; - const schemaProps = getUiOptions(uiSchema).semantic; + const schemaProps = getUiOptions(uiSchema).semantic; const optionProps = options.semantic; // formContext props should overide other props return Object.assign( @@ -67,15 +82,19 @@ export function getSemanticProps({ * @param {Object?} params.defaultProps * @returns {any} */ -export function getSemanticErrorProps({ - formContext = {}, +export function getSemanticErrorProps< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ + formContext = {} as F, uiSchema = {}, options = {}, defaultProps = { size: "small", pointing: "above" }, -}: SemanticErrorPropsType) { +}: SemanticErrorPropsType) { const formContextProps = formContext.semantic && formContext.semantic.errorOptions; - const semanticOptions: GenericObjectType = getUiOptions(uiSchema) + const semanticOptions: GenericObjectType = getUiOptions(uiSchema) .semantic as GenericObjectType; const schemaProps = semanticOptions && semanticOptions.errorOptions; const optionProps = diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 4c05a7530f..77402ac57d 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -680,7 +680,7 @@ export interface WidgetProps< */ options: NonNullable> & { /** The enum options list for a type that supports them */ - enumOptions?: EnumOptionsType[]; + enumOptions?: EnumOptionsType[]; }; /** The `formContext` object that you passed to `Form` */ formContext?: F;