From 89dd3ecde0bc208884f4017d62820f28aecf81e1 Mon Sep 17 00:00:00 2001 From: Nick Grosenbacher Date: Sat, 7 Jan 2023 10:26:59 -0500 Subject: [PATCH] Typescript generics in fluent-ui - Partially fixes #3072 --- CHANGELOG.md | 1 + .../fluent-ui/src/AddButton/AddButton.tsx | 35 +++++++----- .../ArrayFieldItemTemplate.tsx | 17 ++++-- .../ArrayFieldTemplate/ArrayFieldTemplate.tsx | 46 +++++++++------- .../BaseInputTemplate/BaseInputTemplate.tsx | 22 +++++--- .../src/CheckboxWidget/CheckboxWidget.tsx | 17 ++++-- .../src/CheckboxesWidget/CheckboxesWidget.tsx | 19 ++++--- .../fluent-ui/src/ColorWidget/ColorWidget.tsx | 23 ++++---- .../src/DateTimeWidget/DateTimeWidget.tsx | 22 +++++--- .../fluent-ui/src/DateWidget/DateWidget.tsx | 20 ++++--- .../src/DescriptionField/DescriptionField.tsx | 17 ++++-- .../fluent-ui/src/ErrorList/ErrorList.tsx | 47 +++++++++------- .../FieldErrorTemplate/FieldErrorTemplate.tsx | 13 ++++- .../FieldHelpTemplate/FieldHelpTemplate.tsx | 13 ++++- .../src/FieldTemplate/FieldTemplate.tsx | 30 +++++++---- packages/fluent-ui/src/FuiForm/FuiForm.ts | 15 ++++++ packages/fluent-ui/src/FuiForm/FuiForm.tsx | 8 --- .../fluent-ui/src/IconButton/IconButton.tsx | 37 ++++++++++--- .../ObjectFieldTemplate.tsx | 28 ++++++---- .../fluent-ui/src/RadioWidget/RadioWidget.tsx | 19 ++++--- .../fluent-ui/src/RangeWidget/RangeWidget.tsx | 25 +++++---- .../src/SelectWidget/SelectWidget.tsx | 26 +++++---- .../src/SubmitButton/SubmitButton.tsx | 16 ++++-- packages/fluent-ui/src/Templates/Templates.ts | 54 ++++++++++++------- .../src/TextareaWidget/TextareaWidget.tsx | 23 +++++--- packages/fluent-ui/src/Theme/Theme.ts | 21 +++++--- .../fluent-ui/src/TitleField/TitleField.tsx | 25 ++++++--- .../src/UpDownWidget/UpDownWidget.tsx | 23 +++++--- packages/fluent-ui/src/Widgets/Widgets.ts | 39 +++++++++----- .../WrapIfAdditionalTemplate.tsx | 15 ++++-- packages/fluent-ui/src/index.ts | 8 +-- 31 files changed, 483 insertions(+), 241 deletions(-) create mode 100644 packages/fluent-ui/src/FuiForm/FuiForm.ts delete mode 100644 packages/fluent-ui/src/FuiForm/FuiForm.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4f19f344..865916d4ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ should change the heading of the (upcoming) version to include a major version b ## @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) - 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/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) diff --git a/packages/fluent-ui/src/AddButton/AddButton.tsx b/packages/fluent-ui/src/AddButton/AddButton.tsx index 9768974763..66b3e21f7d 100644 --- a/packages/fluent-ui/src/AddButton/AddButton.tsx +++ b/packages/fluent-ui/src/AddButton/AddButton.tsx @@ -1,18 +1,27 @@ import React from "react"; -import { IconButtonProps } from "@rjsf/utils"; +import { + FormContextType, + IconButtonProps, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; import { IIconProps, CommandBarButton } from "@fluentui/react"; const addIcon: IIconProps = { iconName: "Add" }; -const AddButton = (props: IconButtonProps) => ( - -); - -export default AddButton; +export default function AddButton< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: IconButtonProps) { + return ( + + ); +} diff --git a/packages/fluent-ui/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx b/packages/fluent-ui/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx index 021def7afa..425d907b21 100644 --- a/packages/fluent-ui/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx +++ b/packages/fluent-ui/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx @@ -1,7 +1,16 @@ import React from "react"; -import { ArrayFieldTemplateItemType } from "@rjsf/utils"; +import { + ArrayFieldTemplateItemType, + FormContextType, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; -const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => { +export default function ArrayFieldItemTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldTemplateItemType) { const { children, disabled, @@ -58,6 +67,4 @@ const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => { ); -}; - -export default ArrayFieldItemTemplate; +} diff --git a/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx index a88d0adc88..7f3f25a5eb 100644 --- a/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx +++ b/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx @@ -4,13 +4,20 @@ import { getUiOptions, ArrayFieldTemplateItemType, ArrayFieldTemplateProps, + StrictRJSFSchema, + RJSFSchema, + FormContextType, } from "@rjsf/utils"; const rightJustify = { float: "right", } as React.CSSProperties; -const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { +export default function ArrayFieldTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldTemplateProps) { const { canAdd, disabled, @@ -24,23 +31,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(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 }, @@ -63,9 +71,11 @@ const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { registry={registry} /> {items.length > 0 && - items.map(({ key, ...itemProps }: ArrayFieldTemplateItemType) => ( - - ))} + items.map( + ({ key, ...itemProps }: ArrayFieldTemplateItemType) => ( + + ) + )} {canAdd && ( { )} ); -}; - -export default ArrayFieldTemplate; +} diff --git a/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx b/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx index c71f93566e..2cc9e233d8 100644 --- a/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx +++ b/packages/fluent-ui/src/BaseInputTemplate/BaseInputTemplate.tsx @@ -1,6 +1,12 @@ import React from "react"; import { TextField } from "@fluentui/react"; -import { getInputProps, WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + getInputProps, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import _pick from "lodash/pick"; // Keys of ITextFieldProps from @fluentui/react @@ -44,7 +50,11 @@ const allowedProps = [ "list", ]; -const BaseInputTemplate = ({ +export default function BaseInputTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ id, placeholder, required, @@ -61,8 +71,8 @@ const BaseInputTemplate = ({ type, rawErrors, multiline, -}: WidgetProps) => { - const inputProps = getInputProps(schema, type, options); +}: WidgetProps) { + const inputProps = getInputProps(schema, type, options); const _onChange = ({ target: { value }, }: React.ChangeEvent) => @@ -109,6 +119,4 @@ const BaseInputTemplate = ({ )} ); -}; - -export default BaseInputTemplate; +} diff --git a/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx b/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx index f8d86c7ef8..94dd6fe0f3 100644 --- a/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx +++ b/packages/fluent-ui/src/CheckboxWidget/CheckboxWidget.tsx @@ -1,6 +1,11 @@ import React from "react"; import { Checkbox } from "@fluentui/react"; -import { WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import _pick from "lodash/pick"; // Keys of ICheckboxProps from @fluentui/react @@ -27,7 +32,11 @@ export const allowedProps = [ "theme", ]; -const CheckboxWidget = (props: WidgetProps) => { +export default function CheckboxWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WidgetProps) { const { id, value, @@ -75,6 +84,4 @@ const CheckboxWidget = (props: WidgetProps) => { /> ); -}; - -export default CheckboxWidget; +} diff --git a/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx b/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx index 8f74121537..57d2cd507f 100644 --- a/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx +++ b/packages/fluent-ui/src/CheckboxesWidget/CheckboxesWidget.tsx @@ -1,6 +1,11 @@ import React from "react"; import { Checkbox, Label } from "@fluentui/react"; -import { WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import { allowedProps } from "../CheckboxWidget"; import _pick from "lodash/pick"; @@ -25,7 +30,11 @@ const deselectValue = (value: any, selected: any) => { return selected.filter((v: any) => v !== value); }; -const CheckboxesWidget = ({ +export default function CheckboxesWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ schema, label, id, @@ -39,7 +48,7 @@ const CheckboxesWidget = ({ onBlur, onFocus, rawErrors = [], -}: WidgetProps) => { +}: WidgetProps) { const { enumOptions, enumDisabled } = options; const _onChange = @@ -95,6 +104,4 @@ const CheckboxesWidget = ({ {(rawErrors || []).join("\n")} ); -}; - -export default CheckboxesWidget; +} diff --git a/packages/fluent-ui/src/ColorWidget/ColorWidget.tsx b/packages/fluent-ui/src/ColorWidget/ColorWidget.tsx index 3ae6686ff4..08380b78a9 100644 --- a/packages/fluent-ui/src/ColorWidget/ColorWidget.tsx +++ b/packages/fluent-ui/src/ColorWidget/ColorWidget.tsx @@ -6,7 +6,12 @@ import { getColorFromString, Label, } from "@fluentui/react"; -import { WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import _pick from "lodash/pick"; const styles_red = { @@ -35,14 +40,11 @@ const allowedProps: (keyof IColorPickerProps)[] = [ "showPreview", ]; -const ColorWidget = ({ - schema, - options, - value, - required, - label, - onChange, -}: WidgetProps) => { +export default function ColorWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ schema, options, value, required, label, onChange }: WidgetProps) { const updateColor = (_ev: any, colorObj: IColor) => { onChange(colorObj.hex); }; @@ -64,5 +66,4 @@ const ColorWidget = ({ /> ); -}; -export default ColorWidget; +} diff --git a/packages/fluent-ui/src/DateTimeWidget/DateTimeWidget.tsx b/packages/fluent-ui/src/DateTimeWidget/DateTimeWidget.tsx index bf6ef42da9..b92f9fc2b2 100644 --- a/packages/fluent-ui/src/DateTimeWidget/DateTimeWidget.tsx +++ b/packages/fluent-ui/src/DateTimeWidget/DateTimeWidget.tsx @@ -1,8 +1,20 @@ import React from "react"; -import { WidgetProps, getTemplate, localToUTC, utcToLocal } from "@rjsf/utils"; +import { + WidgetProps, + getTemplate, + localToUTC, + utcToLocal, + StrictRJSFSchema, + RJSFSchema, + FormContextType, +} from "@rjsf/utils"; -const DateTimeWidget = (props: WidgetProps) => { +export default function DateTimeWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WidgetProps) { const { registry } = props; const uiProps: any = props.options["props"] || {}; const options = { @@ -12,7 +24,7 @@ const DateTimeWidget = (props: WidgetProps) => { ...uiProps, }, }; - const BaseInputTemplate = getTemplate<"BaseInputTemplate">( + const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, S, F>( "BaseInputTemplate", registry, options @@ -31,6 +43,4 @@ const DateTimeWidget = (props: WidgetProps) => { onChange={onChange} /> ); -}; - -export default DateTimeWidget; +} diff --git a/packages/fluent-ui/src/DateWidget/DateWidget.tsx b/packages/fluent-ui/src/DateWidget/DateWidget.tsx index 3a6fd01fd4..2295b5e07b 100644 --- a/packages/fluent-ui/src/DateWidget/DateWidget.tsx +++ b/packages/fluent-ui/src/DateWidget/DateWidget.tsx @@ -1,5 +1,11 @@ import React from "react"; -import { WidgetProps, pad } from "@rjsf/utils"; +import { + WidgetProps, + pad, + StrictRJSFSchema, + RJSFSchema, + FormContextType, +} from "@rjsf/utils"; import { DatePicker, DayOfWeek, mergeStyleSets } from "@fluentui/react"; import _pick from "lodash/pick"; @@ -75,7 +81,11 @@ const parseDate = (dateStr?: string) => { return dt; }; -const DateWidget = ({ +export default function DateWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ id, required, label, @@ -85,7 +95,7 @@ const DateWidget = ({ onFocus, options, placeholder, -}: WidgetProps) => { +}: WidgetProps) { const _onSelectDate = (date: Date | null | undefined) => { if (date) { const formatted = formatDate(date); @@ -115,6 +125,4 @@ const DateWidget = ({ {...uiProps} /> ); -}; - -export default DateWidget; +} diff --git a/packages/fluent-ui/src/DescriptionField/DescriptionField.tsx b/packages/fluent-ui/src/DescriptionField/DescriptionField.tsx index 71190d0bfb..c7f8eab79b 100644 --- a/packages/fluent-ui/src/DescriptionField/DescriptionField.tsx +++ b/packages/fluent-ui/src/DescriptionField/DescriptionField.tsx @@ -1,13 +1,20 @@ import React from "react"; -import { DescriptionFieldProps } from "@rjsf/utils"; +import { + DescriptionFieldProps, + FormContextType, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; import { Text } from "@fluentui/react"; -const DescriptionField = ({ description, id }: DescriptionFieldProps) => { +export default function DescriptionField< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ description, id }: DescriptionFieldProps) { if (description) { return {description}; } return null; -}; - -export default DescriptionField; +} diff --git a/packages/fluent-ui/src/ErrorList/ErrorList.tsx b/packages/fluent-ui/src/ErrorList/ErrorList.tsx index 94b9d345ea..43edfc49cb 100644 --- a/packages/fluent-ui/src/ErrorList/ErrorList.tsx +++ b/packages/fluent-ui/src/ErrorList/ErrorList.tsx @@ -1,22 +1,31 @@ import React from "react"; import { MessageBar, MessageBarType } from "@fluentui/react"; -import { ErrorListProps } from "@rjsf/utils"; +import { + ErrorListProps, + FormContextType, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; -const ErrorList = ({ errors }: ErrorListProps) => ( - <> - {errors.map((error, i) => { - return ( - - {error.stack} - - ); - })} - -); - -export default ErrorList; +export default function ErrorList< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ errors }: ErrorListProps) { + return ( + <> + {errors.map((error, i) => { + return ( + + {error.stack} + + ); + })} + + ); +} diff --git a/packages/fluent-ui/src/FieldErrorTemplate/FieldErrorTemplate.tsx b/packages/fluent-ui/src/FieldErrorTemplate/FieldErrorTemplate.tsx index f59e0896ec..c3c6a842f4 100644 --- a/packages/fluent-ui/src/FieldErrorTemplate/FieldErrorTemplate.tsx +++ b/packages/fluent-ui/src/FieldErrorTemplate/FieldErrorTemplate.tsx @@ -1,12 +1,21 @@ import React from "react"; -import { FieldErrorProps } from "@rjsf/utils"; +import { + FieldErrorProps, + FormContextType, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; import { List } from "@fluentui/react"; /** The `FieldErrorTemplate` component renders the errors local to the particular field * * @param props - The `FieldErrorProps` for the errors being rendered */ -export default function FieldErrorTemplate(props: FieldErrorProps) { +export default function FieldErrorTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: FieldErrorProps) { const { errors = [], idSchema } = props; if (errors.length === 0) { return null; diff --git a/packages/fluent-ui/src/FieldHelpTemplate/FieldHelpTemplate.tsx b/packages/fluent-ui/src/FieldHelpTemplate/FieldHelpTemplate.tsx index 13980e2e47..244e51fad9 100644 --- a/packages/fluent-ui/src/FieldHelpTemplate/FieldHelpTemplate.tsx +++ b/packages/fluent-ui/src/FieldHelpTemplate/FieldHelpTemplate.tsx @@ -1,12 +1,21 @@ import React from "react"; -import { FieldHelpProps } from "@rjsf/utils"; +import { + FieldHelpProps, + FormContextType, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; import { Text } from "@fluentui/react"; /** The `FieldHelpTemplate` component renders any help desired for a field * * @param props - The `FieldHelpProps` to be rendered */ -export default function FieldHelpTemplate(props: FieldHelpProps) { +export default function FieldHelpTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: FieldHelpProps) { const { idSchema, help } = props; if (!help) { return null; diff --git a/packages/fluent-ui/src/FieldTemplate/FieldTemplate.tsx b/packages/fluent-ui/src/FieldTemplate/FieldTemplate.tsx index 83a302e9c7..a6dde0890a 100644 --- a/packages/fluent-ui/src/FieldTemplate/FieldTemplate.tsx +++ b/packages/fluent-ui/src/FieldTemplate/FieldTemplate.tsx @@ -1,8 +1,19 @@ import React from "react"; -import { FieldTemplateProps, getTemplate, getUiOptions } from "@rjsf/utils"; +import { + FieldTemplateProps, + FormContextType, + getTemplate, + getUiOptions, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; import { Text } from "@fluentui/react"; -const FieldTemplate = (props: FieldTemplateProps) => { +export default function FieldTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: FieldTemplateProps) { const { id, children, @@ -13,12 +24,13 @@ const FieldTemplate = (props: FieldTemplateProps) => { uiSchema, registry, } = props; - const uiOptions = getUiOptions(uiSchema); - const WrapIfAdditionalTemplate = getTemplate<"WrapIfAdditionalTemplate">( + const uiOptions = getUiOptions(uiSchema); + const WrapIfAdditionalTemplate = getTemplate< "WrapIfAdditionalTemplate", - registry, - uiOptions - ); + T, + S, + F + >("WrapIfAdditionalTemplate", registry, uiOptions); // TODO: do this better by not returning the form-group class from master. let { classNames = "" } = props; classNames = "ms-Grid-col ms-sm12 " + classNames.replace("form-group", ""); @@ -36,6 +48,4 @@ const FieldTemplate = (props: FieldTemplateProps) => { ); -}; - -export default FieldTemplate; +} diff --git a/packages/fluent-ui/src/FuiForm/FuiForm.ts b/packages/fluent-ui/src/FuiForm/FuiForm.ts new file mode 100644 index 0000000000..8db1a897f5 --- /dev/null +++ b/packages/fluent-ui/src/FuiForm/FuiForm.ts @@ -0,0 +1,15 @@ +import { ComponentType } from "react"; +import { FormProps, withTheme } from "@rjsf/core"; + +import { generateTheme } from "../Theme"; +import { FormContextType, RJSFSchema, StrictRJSFSchema } from "@rjsf/utils"; + +export function generateForm< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): ComponentType> { + return withTheme(generateTheme()); +} + +export default generateForm(); diff --git a/packages/fluent-ui/src/FuiForm/FuiForm.tsx b/packages/fluent-ui/src/FuiForm/FuiForm.tsx deleted file mode 100644 index 3c85392e75..0000000000 --- a/packages/fluent-ui/src/FuiForm/FuiForm.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { ComponentType } from "react"; -import { withTheme, FormProps } from "@rjsf/core"; - -import Theme from "../Theme"; - -const FuiForm: ComponentType = withTheme(Theme); - -export default FuiForm; diff --git a/packages/fluent-ui/src/IconButton/IconButton.tsx b/packages/fluent-ui/src/IconButton/IconButton.tsx index ac114550d1..ff1565f463 100644 --- a/packages/fluent-ui/src/IconButton/IconButton.tsx +++ b/packages/fluent-ui/src/IconButton/IconButton.tsx @@ -1,8 +1,17 @@ import React from "react"; import { IconButton, IIconProps } from "@fluentui/react"; -import { IconButtonProps } from "@rjsf/utils"; +import { + FormContextType, + IconButtonProps, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; -export default function FluentIconButton(props: IconButtonProps) { +export default function FluentIconButton< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: IconButtonProps) { const iconProps: IIconProps = { iconName: props.icon as string, }; @@ -17,14 +26,26 @@ export default function FluentIconButton(props: IconButtonProps) { ); } -export function MoveDownButton(props: IconButtonProps) { - return ; +export function MoveDownButton< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: IconButtonProps) { + return title="Move down" {...props} icon="Down" />; } -export function MoveUpButton(props: IconButtonProps) { - return ; +export function MoveUpButton< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: IconButtonProps) { + return title="Move up" {...props} icon="Up" />; } -export function RemoveButton(props: IconButtonProps) { - return ; +export function RemoveButton< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: IconButtonProps) { + return title="Remove" {...props} icon="Delete" />; } diff --git a/packages/fluent-ui/src/ObjectFieldTemplate/ObjectFieldTemplate.tsx b/packages/fluent-ui/src/ObjectFieldTemplate/ObjectFieldTemplate.tsx index 2dc176cd21..25f25b5bab 100644 --- a/packages/fluent-ui/src/ObjectFieldTemplate/ObjectFieldTemplate.tsx +++ b/packages/fluent-ui/src/ObjectFieldTemplate/ObjectFieldTemplate.tsx @@ -1,11 +1,18 @@ import React from "react"; import { + FormContextType, getTemplate, getUiOptions, ObjectFieldTemplateProps, + RJSFSchema, + StrictRJSFSchema, } from "@rjsf/utils"; -const ObjectFieldTemplate = ({ +export default function ObjectFieldTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ description, title, properties, @@ -14,18 +21,19 @@ const ObjectFieldTemplate = ({ uiSchema, idSchema, registry, -}: ObjectFieldTemplateProps) => { - const uiOptions = getUiOptions(uiSchema); - const TitleFieldTemplate = getTemplate<"TitleFieldTemplate">( +}: ObjectFieldTemplateProps) { + const uiOptions = getUiOptions(uiSchema); + const TitleFieldTemplate = getTemplate<"TitleFieldTemplate", T, S, F>( "TitleFieldTemplate", registry, uiOptions ); - const DescriptionFieldTemplate = getTemplate<"DescriptionFieldTemplate">( + const DescriptionFieldTemplate = getTemplate< "DescriptionFieldTemplate", - registry, - uiOptions - ); + T, + S, + F + >("DescriptionFieldTemplate", registry, uiOptions); return ( <> {(uiOptions.title || title) && ( @@ -54,6 +62,4 @@ const ObjectFieldTemplate = ({ ); -}; - -export default ObjectFieldTemplate; +} diff --git a/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx b/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx index bd22a2d096..21b8cf7ef4 100644 --- a/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx +++ b/packages/fluent-ui/src/RadioWidget/RadioWidget.tsx @@ -4,7 +4,12 @@ import { IChoiceGroupOption, IChoiceGroupProps, } from "@fluentui/react"; -import { WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import _pick from "lodash/pick"; const allowedProps: (keyof IChoiceGroupProps)[] = [ @@ -20,7 +25,11 @@ const allowedProps: (keyof IChoiceGroupProps)[] = [ "ariaLabelledBy", ]; -const RadioWidget = ({ +export default function RadioWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ id, schema, options, @@ -32,7 +41,7 @@ const RadioWidget = ({ onFocus, disabled, readonly, -}: WidgetProps) => { +}: WidgetProps) { const { enumOptions, enumDisabled } = options; function _onChange( @@ -78,6 +87,4 @@ const RadioWidget = ({ {...uiProps} /> ); -}; - -export default RadioWidget; +} diff --git a/packages/fluent-ui/src/RangeWidget/RangeWidget.tsx b/packages/fluent-ui/src/RangeWidget/RangeWidget.tsx index b09a3905d8..986e744fe2 100644 --- a/packages/fluent-ui/src/RangeWidget/RangeWidget.tsx +++ b/packages/fluent-ui/src/RangeWidget/RangeWidget.tsx @@ -1,7 +1,13 @@ import React from "react"; import { Slider, Label } from "@fluentui/react"; -import { rangeSpec, WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + rangeSpec, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import _pick from "lodash/pick"; const styles_red = { @@ -36,21 +42,22 @@ const allowedProps = [ "originFromZero", ]; -const RangeWidget = ({ +export default function RangeWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ value, readonly, disabled, options, schema, - //formContext, - //registry, - //rawErrors, onChange, required, label, id, -}: WidgetProps) => { - const sliderProps = { value, label, id, ...rangeSpec(schema) }; +}: WidgetProps) { + const sliderProps = { value, label, id, ...rangeSpec(schema) }; const _onChange = (value: number) => onChange(value); @@ -72,6 +79,4 @@ const RangeWidget = ({ /> ); -}; - -export default RangeWidget; +} diff --git a/packages/fluent-ui/src/SelectWidget/SelectWidget.tsx b/packages/fluent-ui/src/SelectWidget/SelectWidget.tsx index 794f225694..07f7908123 100644 --- a/packages/fluent-ui/src/SelectWidget/SelectWidget.tsx +++ b/packages/fluent-ui/src/SelectWidget/SelectWidget.tsx @@ -1,6 +1,12 @@ import React from "react"; import { Label, Dropdown, IDropdownOption } from "@fluentui/react"; -import { WidgetProps, processSelectValue } from "@rjsf/utils"; +import { + WidgetProps, + processSelectValue, + StrictRJSFSchema, + RJSFSchema, + FormContextType, +} from "@rjsf/utils"; import _pick from "lodash/pick"; // Keys of IDropdownProps from @fluentui/react @@ -49,7 +55,11 @@ const allowedProps = [ "openOnKeyboardFocus", ]; -const SelectWidget = ({ +export default function SelectWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ schema, id, options, @@ -62,7 +72,7 @@ const SelectWidget = ({ onChange, onBlur, onFocus, -}: WidgetProps) => { +}: WidgetProps) { const { enumOptions, enumDisabled } = options; const _onChange = ( @@ -80,14 +90,14 @@ const SelectWidget = ({ onChange(valueOrDefault.filter((key: any) => key !== item.key)); } } else { - onChange(processSelectValue(schema, item.key, options)); + onChange(processSelectValue(schema, item.key, options)); } }; const _onBlur = (e: any) => - onBlur(id, processSelectValue(schema, e.target.value, options)); + onBlur(id, processSelectValue(schema, e.target.value, options)); const _onFocus = (e: any) => - onFocus(id, processSelectValue(schema, e.target.value, options)); + onFocus(id, processSelectValue(schema, e.target.value, options)); const newOptions = (enumOptions as { value: any; label: any }[]).map( (option) => ({ @@ -116,6 +126,4 @@ const SelectWidget = ({ /> ); -}; - -export default SelectWidget; +} diff --git a/packages/fluent-ui/src/SubmitButton/SubmitButton.tsx b/packages/fluent-ui/src/SubmitButton/SubmitButton.tsx index 793773d953..72899cab36 100644 --- a/packages/fluent-ui/src/SubmitButton/SubmitButton.tsx +++ b/packages/fluent-ui/src/SubmitButton/SubmitButton.tsx @@ -1,8 +1,18 @@ import React from "react"; -import { getSubmitButtonOptions, SubmitButtonProps } from "@rjsf/utils"; +import { + FormContextType, + getSubmitButtonOptions, + RJSFSchema, + StrictRJSFSchema, + SubmitButtonProps, +} from "@rjsf/utils"; import { PrimaryButton } from "@fluentui/react"; -export default ({ uiSchema }: SubmitButtonProps) => { +export default function SubmitButton< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ uiSchema }: SubmitButtonProps) { const { submitText, norender, @@ -19,4 +29,4 @@ export default ({ uiSchema }: SubmitButtonProps) => { ); -}; +} diff --git a/packages/fluent-ui/src/Templates/Templates.ts b/packages/fluent-ui/src/Templates/Templates.ts index 2c373e17d8..b4b5648b07 100644 --- a/packages/fluent-ui/src/Templates/Templates.ts +++ b/packages/fluent-ui/src/Templates/Templates.ts @@ -12,24 +12,38 @@ import ObjectFieldTemplate from "../ObjectFieldTemplate"; import SubmitButton from "../SubmitButton"; import TitleField from "../TitleField"; import WrapIfAdditionalTemplate from "../WrapIfAdditionalTemplate"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + TemplatesType, +} from "@rjsf/utils"; -export default { - 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 generateTemplates(); diff --git a/packages/fluent-ui/src/TextareaWidget/TextareaWidget.tsx b/packages/fluent-ui/src/TextareaWidget/TextareaWidget.tsx index 120129de28..fdb2916320 100644 --- a/packages/fluent-ui/src/TextareaWidget/TextareaWidget.tsx +++ b/packages/fluent-ui/src/TextareaWidget/TextareaWidget.tsx @@ -1,16 +1,25 @@ import React from "react"; -import { getTemplate, getUiOptions, WidgetProps } from "@rjsf/utils"; +import { + FormContextType, + getTemplate, + getUiOptions, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; -const TextareaWidget = (props: WidgetProps) => { +export default function TextareaWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WidgetProps) { const { uiSchema, registry } = props; - const options = getUiOptions(uiSchema); - const BaseInputTemplate = getTemplate<"BaseInputTemplate">( + const options = getUiOptions(uiSchema); + const BaseInputTemplate = getTemplate<"BaseInputTemplate", T, S, F>( "BaseInputTemplate", registry, options ); // TODO: rows and columns. return ; -}; - -export default TextareaWidget; +} diff --git a/packages/fluent-ui/src/Theme/Theme.ts b/packages/fluent-ui/src/Theme/Theme.ts index ecfcdd5720..ec9d319569 100644 --- a/packages/fluent-ui/src/Theme/Theme.ts +++ b/packages/fluent-ui/src/Theme/Theme.ts @@ -1,11 +1,18 @@ import { ThemeProps } from "@rjsf/core"; -import Templates from "../Templates"; -import Widgets from "../Widgets"; +import { generateTemplates } from "../Templates"; +import { generateWidgets } from "../Widgets"; +import { FormContextType, RJSFSchema, StrictRJSFSchema } from "@rjsf/utils"; -const Theme: ThemeProps = { - templates: Templates, - widgets: Widgets, -}; +export function generateTheme< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): ThemeProps { + return { + templates: generateTemplates(), + widgets: generateWidgets(), + }; +} -export default Theme; +export default generateTheme(); diff --git a/packages/fluent-ui/src/TitleField/TitleField.tsx b/packages/fluent-ui/src/TitleField/TitleField.tsx index 1985f2f7e7..6e68e01408 100644 --- a/packages/fluent-ui/src/TitleField/TitleField.tsx +++ b/packages/fluent-ui/src/TitleField/TitleField.tsx @@ -1,5 +1,10 @@ import React from "react"; -import { TitleFieldProps } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + TitleFieldProps, +} from "@rjsf/utils"; import { Label } from "@fluentui/react"; const styles = { @@ -12,10 +17,14 @@ const styles = { ], }; -const TitleField = ({ id, title }: TitleFieldProps) => ( - -); - -export default TitleField; +export default function TitleField< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ id, title }: TitleFieldProps) { + return ( + + ); +} diff --git a/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx b/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx index 4bf580d7a2..14bc6a85c4 100644 --- a/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx +++ b/packages/fluent-ui/src/UpDownWidget/UpDownWidget.tsx @@ -1,7 +1,12 @@ import React from "react"; -import { Label } from "@fluentui/react"; -import { SpinButton } from "@fluentui/react"; -import { WidgetProps, rangeSpec } from "@rjsf/utils"; +import { Label, SpinButton } from "@fluentui/react"; +import { + FormContextType, + rangeSpec, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from "@rjsf/utils"; import _pick from "lodash/pick"; // Keys of ISpinButtonProps from @fluentui/react @@ -44,7 +49,11 @@ const allowedProps = [ "value", ]; -const UpDownWidget = ({ +export default function UpDownWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ id, required, readonly, @@ -57,7 +66,7 @@ const UpDownWidget = ({ options, schema, }: // autofocus, -WidgetProps) => { +WidgetProps) { const _onChange = ({ target: { value }, }: React.ChangeEvent) => onChange(Number(value)); @@ -117,6 +126,4 @@ WidgetProps) => { /> ); -}; - -export default UpDownWidget; +} diff --git a/packages/fluent-ui/src/Widgets/Widgets.ts b/packages/fluent-ui/src/Widgets/Widgets.ts index 106aa60235..b9939a891b 100644 --- a/packages/fluent-ui/src/Widgets/Widgets.ts +++ b/packages/fluent-ui/src/Widgets/Widgets.ts @@ -8,15 +8,30 @@ import RangeWidget from "../RangeWidget/RangeWidget"; import SelectWidget from "../SelectWidget/SelectWidget"; import TextareaWidget from "../TextareaWidget/TextareaWidget"; import UpDownWidget from "../UpDownWidget/UpDownWidget"; -export default { - CheckboxWidget, - CheckboxesWidget, - ColorWidget, - DateWidget, - DateTimeWidget, - RadioWidget, - RangeWidget, - SelectWidget, - TextareaWidget, - UpDownWidget, -}; +import { + FormContextType, + RegistryWidgetsType, + RJSFSchema, + StrictRJSFSchema, +} from "@rjsf/utils"; + +export function generateWidgets< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): RegistryWidgetsType { + return { + CheckboxWidget, + CheckboxesWidget, + ColorWidget, + DateWidget, + DateTimeWidget, + RadioWidget, + RangeWidget, + SelectWidget, + TextareaWidget, + UpDownWidget, + }; +} + +export default generateWidgets(); diff --git a/packages/fluent-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx b/packages/fluent-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx index b7b516a46e..1468feb73a 100644 --- a/packages/fluent-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx +++ b/packages/fluent-ui/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx @@ -1,9 +1,16 @@ import React from "react"; -import { WrapIfAdditionalTemplateProps } from "@rjsf/utils"; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WrapIfAdditionalTemplateProps, +} from "@rjsf/utils"; -export default function WrapIfAdditionalTemplate( - props: WrapIfAdditionalTemplateProps -) { +export default function WrapIfAdditionalTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WrapIfAdditionalTemplateProps) { const { children } = props; // TODO Implement WrapIfAdditionalTemplate features in FluentUI (#2777) return <>{children}; diff --git a/packages/fluent-ui/src/index.ts b/packages/fluent-ui/src/index.ts index 4b96d43dfa..692cd22bfd 100644 --- a/packages/fluent-ui/src/index.ts +++ b/packages/fluent-ui/src/index.ts @@ -1,10 +1,10 @@ import { initializeIcons } from "@fluentui/react"; import FuiForm from "./FuiForm/"; -export { default as Form } from "./FuiForm"; -export { default as Templates } from "./Templates"; -export { default as Theme } from "./Theme"; -export { default as Widgets } from "./Widgets"; +export { default as Form, generateForm } from "./FuiForm"; +export { default as Templates, generateTemplates } from "./Templates"; +export { default as Theme, generateTheme } from "./Theme"; +export { default as Widgets, generateWidgets } from "./Widgets"; initializeIcons();