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 semantic-ui theme #3356

Merged
merged 1 commit into from Jan 7, 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 @@ -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;
}
118 changes: 68 additions & 50 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 All @@ -58,7 +72,7 @@ function ArrayFieldTemplate({
<div
className={cleanClassNames([
className,
isFixedItems(schema) ? "" : "sortable-form-fields",
isFixedItems<S>(schema) ? "" : "sortable-form-fields",
])}
>
<ArrayFieldTitleTemplate
Expand All @@ -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;
24 changes: 19 additions & 5 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 All @@ -20,7 +35,7 @@ function CheckboxWidget(props: WidgetProps) {
uiSchema,
rawErrors = [],
} = props;
const semanticProps = getSemanticProps({
const semanticProps = getSemanticProps<T, S, F>({
options,
formContext,
uiSchema,
Expand All @@ -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;