Skip to content

Commit

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

- In `@rjsf/antd` 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
  - Converted the three components that used the `withConfigConsumer()` HOC to instead use the `<ConfigConsumer>` component to generate the prefixCls
  - Added a `generateForm<T, S, F>()` function to main `index.ts`, exporting it and exporting the default `Form` 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 main `index.ts`, 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 `generateTemplates()` and `generateWidgets()` functions
- Updated the `CHANGELOG.md` accordingly
  • Loading branch information
heath-freenome authored and shijistar committed Jun 8, 2023
1 parent 5a60e14 commit efa66fd
Show file tree
Hide file tree
Showing 28 changed files with 896 additions and 618 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -20,6 +20,8 @@ should change the heading of the (upcoming) version to include a major version b
## @rjsf/antd
- 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)
- Updated the use of the deprecated `withConfigConsumer` with the `ConfigConsumer` component instead, fixing [#3336](https://github.com/rjsf-team/react-jsonschema-form/issues/3336)

## @rjsf/bootstrap-4
- 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
34 changes: 26 additions & 8 deletions packages/antd/src/index.ts
@@ -1,15 +1,33 @@
import { ComponentType } from "react";
import { FormContextType, RJSFSchema, StrictRJSFSchema } from "@rjsf/utils";
import { FormProps, ThemeProps, withTheme } from "@rjsf/core";

import Templates from "./templates";
import Widgets from "./widgets";
import Templates, { generateTemplates } from "./templates";
import Widgets, { generateWidgets } from "./widgets";

export { Templates, Widgets };
export function generateTheme<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(): ThemeProps<T, S, F> {
return {
templates: generateTemplates<T, S, F>(),
widgets: generateWidgets<T, S, F>(),
};
}

export const Theme: ThemeProps = {
templates: Templates,
widgets: Widgets,
};
const Theme = generateTheme();

export const Form: React.ComponentType<FormProps> = withTheme(Theme);
export function generateForm<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(): ComponentType<FormProps<T, S, F>> {
return withTheme<T, S, F>(generateTheme<T, S, F>());
}

const Form = generateForm();

export { Form, Templates, Theme, Widgets, generateTemplates, generateWidgets };

export default Form;
48 changes: 30 additions & 18 deletions packages/antd/src/templates/ArrayFieldItemTemplate/index.tsx
@@ -1,8 +1,13 @@
import React from "react";
import { ArrayFieldTemplateItemType } from "@rjsf/utils";
import Button from "antd/lib/button";
import Col from "antd/lib/col";
import Row from "antd/lib/row";
import {
ArrayFieldTemplateItemType,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const BTN_GRP_STYLE = {
width: "100%",
Expand All @@ -12,20 +17,29 @@ const BTN_STYLE = {
width: "calc(100% / 3)",
};

const ArrayFieldItemTemplate = ({
children,
disabled,
hasMoveDown,
hasMoveUp,
hasRemove,
hasToolbar,
index,
onDropIndexClick,
onReorderClick,
readonly,
registry,
uiSchema,
}: 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,
hasMoveDown,
hasMoveUp,
hasRemove,
hasToolbar,
index,
onDropIndexClick,
onReorderClick,
readonly,
registry,
uiSchema,
} = props;
const { MoveDownButton, MoveUpButton, RemoveButton } =
registry.templates.ButtonTemplates;
const { rowGutter = 24, toolbarAlign = "top" } = registry.formContext;
Expand Down Expand Up @@ -76,6 +90,4 @@ const ArrayFieldItemTemplate = ({
)}
</Row>
);
};

export default ArrayFieldItemTemplate;
}
207 changes: 116 additions & 91 deletions packages/antd/src/templates/ArrayFieldTemplate/index.tsx
Expand Up @@ -3,119 +3,144 @@ import {
getTemplate,
getUiOptions,
ArrayFieldTemplateProps,
ArrayFieldTemplateItemType,
FormContextType,
GenericObjectType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";
import classNames from "classnames";
import Col from "antd/lib/col";
import Row from "antd/lib/row";
import { withConfigConsumer } from "antd/lib/config-provider/context";
import {
ConfigConsumer,
ConfigConsumerProps,
} from "antd/lib/config-provider/context";

const DESCRIPTION_COL_STYLE = {
paddingBottom: "8px",
};

// Add in the `prefixCls` element needed by the `withConfigConsumer` HOC
export type AntdArrayFieldTemplateProps = ArrayFieldTemplateProps & {
prefixCls: string;
};

const ArrayFieldTemplate = ({
canAdd,
className,
disabled,
formContext,
idSchema,
items,
onAddClick,
prefixCls,
readonly,
registry,
required,
schema,
title,
uiSchema,
}: AntdArrayFieldTemplateProps) => {
const uiOptions = getUiOptions(uiSchema);
const ArrayFieldDescriptionTemplate = getTemplate(
"ArrayFieldDescriptionTemplate",
/** 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,
className,
disabled,
formContext,
idSchema,
items,
onAddClick,
readonly,
registry,
uiOptions
);
const ArrayFieldItemTemplate = getTemplate<"ArrayFieldItemTemplate">(
required,
schema,
title,
uiSchema,
} = props;
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 },
} = registry.templates;
const { labelAlign = "right", rowGutter = 24 } = formContext;

const labelClsBasic = `${prefixCls}-item-label`;
const labelColClassName = classNames(
labelClsBasic,
labelAlign === "left" && `${labelClsBasic}-left`
// labelCol.className,
);
const { labelAlign = "right", rowGutter = 24 } =
formContext as GenericObjectType;

return (
<fieldset className={className} id={idSchema.$id}>
<Row gutter={rowGutter}>
{(uiOptions.title || title) && (
<Col className={labelColClassName} span={24}>
<ArrayFieldTitleTemplate
idSchema={idSchema}
required={required}
title={uiOptions.title || title}
schema={schema}
uiSchema={uiSchema}
registry={registry}
/>
</Col>
)}
{(uiOptions.description || schema.description) && (
<Col span={24} style={DESCRIPTION_COL_STYLE}>
<ArrayFieldDescriptionTemplate
description={uiOptions.description || schema.description || ""}
idSchema={idSchema}
schema={schema}
uiSchema={uiSchema}
registry={registry}
/>
</Col>
)}
<Col className="row array-item-list" span={24}>
{items &&
items.map(({ key, ...itemProps }) => (
<ArrayFieldItemTemplate key={key} {...itemProps} />
))}
</Col>
<ConfigConsumer>
{(configProps: ConfigConsumerProps) => {
const { getPrefixCls } = configProps;
const prefixCls = getPrefixCls("form");
const labelClsBasic = `${prefixCls}-item-label`;
const labelColClassName = classNames(
labelClsBasic,
labelAlign === "left" && `${labelClsBasic}-left`
// labelCol.className,
);

{canAdd && (
<Col span={24}>
<Row gutter={rowGutter} justify="end">
<Col flex="192px">
<AddButton
className="array-item-add"
disabled={disabled || readonly}
onClick={onAddClick}
uiSchema={uiSchema}
registry={registry}
/>
return (
<fieldset className={className} id={idSchema.$id}>
<Row gutter={rowGutter}>
{(uiOptions.title || title) && (
<Col className={labelColClassName} span={24}>
<ArrayFieldTitleTemplate
idSchema={idSchema}
required={required}
title={uiOptions.title || title}
schema={schema}
uiSchema={uiSchema}
registry={registry}
/>
</Col>
)}
{(uiOptions.description || schema.description) && (
<Col span={24} style={DESCRIPTION_COL_STYLE}>
<ArrayFieldDescriptionTemplate
description={
uiOptions.description || schema.description || ""
}
idSchema={idSchema}
schema={schema}
uiSchema={uiSchema}
registry={registry}
/>
</Col>
)}
<Col className="row array-item-list" span={24}>
{items &&
items.map(
({
key,
...itemProps
}: ArrayFieldTemplateItemType<T, S, F>) => (
<ArrayFieldItemTemplate key={key} {...itemProps} />
)
)}
</Col>

{canAdd && (
<Col span={24}>
<Row gutter={rowGutter} justify="end">
<Col flex="192px">
<AddButton
className="array-item-add"
disabled={disabled || readonly}
onClick={onAddClick}
uiSchema={uiSchema}
registry={registry}
/>
</Col>
</Row>
</Col>
)}
</Row>
</Col>
)}
</Row>
</fieldset>
</fieldset>
);
}}
</ConfigConsumer>
);
};

export default withConfigConsumer<AntdArrayFieldTemplateProps>({
prefixCls: "form",
})(ArrayFieldTemplate);
}

0 comments on commit efa66fd

Please sign in to comment.