Skip to content

Commit

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

- In `@rjsf/utils` added a missing generic to the `EnumOptionsType` in the `WidgetProps`
- 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 `SemanticUIForm.tsx`, exporting it and exporting the default `SemanticUIForm` 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 types and functions in the `utils.ts` file to add the generics
- Updated the `CHANGELOG.md` accordingly
  • Loading branch information
heath-freenome committed Jan 7, 2023
1 parent 08cb1b2 commit 748b6f8
Show file tree
Hide file tree
Showing 28 changed files with 598 additions and 286 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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)
Expand Down
17 changes: 13 additions & 4 deletions 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<T, S, F>) {
return (
<Button
title="Add Item"
Expand All @@ -15,5 +26,3 @@ function AddButton({ uiSchema, registry, color, ...props }: IconButtonProps) {
</Button>
);
}

export default AddButton;
@@ -1,7 +1,10 @@
import React from "react";
import {
ArrayFieldTemplateItemType,
FormContextType,
GenericObjectType,
RJSFSchema,
StrictRJSFSchema,
getUiOptions,
} from "@rjsf/utils";
import { Button, Grid, Segment } from "semantic-ui-react";
Expand All @@ -13,7 +16,15 @@ const gridStyle = (vertical: boolean) => ({
gridTemplateColumns: `1fr ${vertical ? 65 : 110}px`,
});

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 All @@ -30,7 +41,7 @@ const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
} = props;
const { MoveDownButton, MoveUpButton, RemoveButton } =
registry.templates.ButtonTemplates;
const uiOptions = getUiOptions(uiSchema);
const uiOptions = getUiOptions<T, S, F>(uiSchema);
// Pull the semantic props out of the uiOptions that were put in via the ArrayFieldTemplate
const { horizontalButtons = false, wrapItem = false } =
uiOptions.semantic as GenericObjectType;
Expand Down Expand Up @@ -86,6 +97,4 @@ const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
</MaybeWrap>
</div>
);
};

export default ArrayFieldItemTemplate;
}
116 changes: 67 additions & 49 deletions packages/semantic-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx
Expand Up @@ -4,52 +4,66 @@ import {
getUiOptions,
isFixedItems,
ArrayFieldTemplateProps,
ArrayFieldTemplateItemType,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
UI_OPTIONS_KEY,
} from "@rjsf/utils";

import { cleanClassNames, getSemanticProps } from "../util";

function ArrayFieldTemplate({
uiSchema,
idSchema,
canAdd,
className,
// classNames, This is not part of the type, so it is likely never passed in
disabled,
formContext,
items,
onAddClick,
// options, This is not part of the type, so it is likely never passed in
readonly,
required,
schema,
title,
registry,
}: ArrayFieldTemplateProps) {
const semanticProps = getSemanticProps({
/** 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 {
uiSchema,
idSchema,
canAdd,
className,
// classNames, This is not part of the type, so it is likely never passed in
disabled,
formContext,
items,
onAddClick,
// options, This is not part of the type, so it is likely never passed in
readonly,
required,
schema,
title,
registry,
} = props;
const semanticProps = getSemanticProps<T, S, F>({
uiSchema,
formContext,
defaultSchemaProps: { horizontalButtons: false, wrapItem: false },
});
const { horizontalButtons, wrapItem } = semanticProps;
const semantic = { horizontalButtons, wrapItem };
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 Down Expand Up @@ -79,23 +93,29 @@ function ArrayFieldTemplate({
<div key={`array-item-list-${idSchema.$id}`}>
<div className="row array-item-list">
{items &&
items.map(({ key, uiSchema: itemUiSchema = {}, ...props }) => {
// Merge in the semantic props from the ArrayFieldTemplate into each of the items
const mergedUiSchema = {
...itemUiSchema,
[UI_OPTIONS_KEY]: {
...itemUiSchema[UI_OPTIONS_KEY],
semantic,
},
};
return (
<ArrayFieldItemTemplate
key={key}
{...props}
uiSchema={mergedUiSchema}
/>
);
})}
items.map(
({
key,
uiSchema: itemUiSchema = {},
...props
}: ArrayFieldTemplateItemType<T, S, F>) => {
// Merge in the semantic props from the ArrayFieldTemplate into each of the items
const mergedUiSchema = {
...itemUiSchema,
[UI_OPTIONS_KEY]: {
...itemUiSchema[UI_OPTIONS_KEY],
semantic,
},
};
return (
<ArrayFieldItemTemplate
key={key}
{...props}
uiSchema={mergedUiSchema}
/>
);
}
)}
</div>
{canAdd && (
<div
Expand All @@ -117,5 +137,3 @@ function ArrayFieldTemplate({
</div>
);
}

export default ArrayFieldTemplate;
25 changes: 20 additions & 5 deletions packages/semantic-ui/src/BaseInputTemplate/BaseInputTemplate.tsx
@@ -1,9 +1,25 @@
import React from "react";
import { Form } from "semantic-ui-react";
import { getSemanticProps } from "../util";
import { getInputProps, WidgetProps } from "@rjsf/utils";
import {
getInputProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from "@rjsf/utils";

function BaseInputTemplate(props: WidgetProps) {
/** 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,
Expand All @@ -24,8 +40,8 @@ function BaseInputTemplate(props: WidgetProps) {
registry,
rawErrors = [],
} = props;
const inputProps = getInputProps(schema, type, options);
const semanticProps = getSemanticProps({
const inputProps = getInputProps<T, S, F>(schema, type, options);
const semanticProps = getSemanticProps<T, S, F>({
uiSchema,
formContext,
options,
Expand Down Expand Up @@ -71,4 +87,3 @@ function BaseInputTemplate(props: WidgetProps) {
</>
);
}
export default BaseInputTemplate;
22 changes: 18 additions & 4 deletions packages/semantic-ui/src/CheckboxWidget/CheckboxWidget.tsx
@@ -1,9 +1,24 @@
import React from "react";
import { WidgetProps, schemaRequiresTrueValue } from "@rjsf/utils";
import {
schemaRequiresTrueValue,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from "@rjsf/utils";
import { Form, CheckboxProps } from "semantic-ui-react";
import { getSemanticProps } from "../util";

function 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 {
id,
value,
Expand Down Expand Up @@ -31,7 +46,7 @@ function 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 = (
_: React.FormEvent<HTMLInputElement>,
data: CheckboxProps
Expand All @@ -56,4 +71,3 @@ function CheckboxWidget(props: WidgetProps) {
/>
);
}
export default CheckboxWidget;

0 comments on commit 748b6f8

Please sign in to comment.