Skip to content

Commit

Permalink
fix: Add generic type support to the bootstrap-4 theme (#3326)
Browse files Browse the repository at this point in the history
* Generic support for Bootstrap 4
- Partially fix 3072

* Changes from code review

* Changes from code review

Co-authored-by: Heath C <51679588+heath-freenome@users.noreply.github.com>
  • Loading branch information
nickgros and heath-freenome committed Jan 5, 2023
1 parent 56267c8 commit 51a6517
Show file tree
Hide file tree
Showing 27 changed files with 440 additions and 215 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -27,6 +27,7 @@ should change the heading of the (upcoming) version to include a major version b
- 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 `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)
- 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/chakra-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)
Expand Down
39 changes: 22 additions & 17 deletions packages/bootstrap-4/src/AddButton/AddButton.tsx
@@ -1,21 +1,26 @@
import React from "react";
import { IconButtonProps } from "@rjsf/utils";
import {
FormContextType,
IconButtonProps,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";
import Button from "react-bootstrap/Button";
import { BsPlus } from "@react-icons/all-files/bs/BsPlus";

const AddButton: React.ComponentType<IconButtonProps> = ({
uiSchema,
registry,
...props
}) => (
<Button
{...props}
style={{ width: "100%" }}
className={`ml-1 ${props.className}`}
title="Add Item"
>
<BsPlus />
</Button>
);

export default AddButton;
export default function AddButton<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({ uiSchema, registry, ...props }: IconButtonProps<T, S, F>) {
return (
<Button
{...props}
style={{ width: "100%" }}
className={`ml-1 ${props.className}`}
title="Add Item"
>
<BsPlus />
</Button>
);
}
@@ -1,9 +1,18 @@
import React, { CSSProperties } from "react";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
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<T, S, F>) {
const {
children,
disabled,
Expand Down Expand Up @@ -75,6 +84,4 @@ const ArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
</Row>
</div>
);
};

export default ArrayFieldItemTemplate;
}
49 changes: 30 additions & 19 deletions packages/bootstrap-4/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx
Expand Up @@ -5,11 +5,18 @@ import Container from "react-bootstrap/Container";
import {
ArrayFieldTemplateItemType,
ArrayFieldTemplateProps,
FormContextType,
getTemplate,
getUiOptions,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
export default function ArrayFieldTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: ArrayFieldTemplateProps<T, S, F>) {
const {
canAdd,
disabled,
Expand All @@ -23,23 +30,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<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 @@ -69,9 +77,14 @@ const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
className="p-0 m-0"
>
{items &&
items.map(({ key, ...itemProps }: ArrayFieldTemplateItemType) => (
<ArrayFieldItemTemplate key={key} {...itemProps} />
))}
items.map(
({
key,
...itemProps
}: ArrayFieldTemplateItemType<T, S, F>) => (
<ArrayFieldItemTemplate key={key} {...itemProps} />
)
)}
{canAdd && (
<Container className="">
<Row className="mt-2">
Expand All @@ -93,6 +106,4 @@ const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
</Row>
</div>
);
};

export default ArrayFieldTemplate;
}
25 changes: 18 additions & 7 deletions packages/bootstrap-4/src/BaseInputTemplate/BaseInputTemplate.tsx
@@ -1,8 +1,18 @@
import React from "react";
import Form from "react-bootstrap/Form";
import { getInputProps, WidgetProps } from "@rjsf/utils";
import {
FormContextType,
getInputProps,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from "@rjsf/utils";

const BaseInputTemplate = ({
export default function BaseInputTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({
id,
placeholder,
required,
Expand All @@ -19,8 +29,11 @@ const BaseInputTemplate = ({
rawErrors = [],
children,
extraProps,
}: WidgetProps) => {
const inputProps = { ...extraProps, ...getInputProps(schema, type, options) };
}: WidgetProps<T, S, F>) {
const inputProps = {
...extraProps,
...getInputProps<T, S, F>(schema, type, options),
};
const _onChange = ({
target: { value },
}: React.ChangeEvent<HTMLInputElement>) =>
Expand Down Expand Up @@ -62,6 +75,4 @@ const BaseInputTemplate = ({
) : null}
</>
);
};

export default BaseInputTemplate;
}
21 changes: 14 additions & 7 deletions packages/bootstrap-4/src/CheckboxWidget/CheckboxWidget.tsx
@@ -1,9 +1,18 @@
import React from "react";

import { WidgetProps, schemaRequiresTrueValue } from "@rjsf/utils";
import {
WidgetProps,
schemaRequiresTrueValue,
StrictRJSFSchema,
RJSFSchema,
FormContextType,
} from "@rjsf/utils";
import Form from "react-bootstrap/Form";

const CheckboxWidget = (props: WidgetProps) => {
export default function CheckboxWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: WidgetProps<T, S, F>) {
const {
id,
value,
Expand All @@ -19,7 +28,7 @@ const 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 = ({
target: { checked },
Expand Down Expand Up @@ -51,6 +60,4 @@ const CheckboxWidget = (props: WidgetProps) => {
/>
</Form.Group>
);
};

export default CheckboxWidget;
}
19 changes: 13 additions & 6 deletions packages/bootstrap-4/src/CheckboxesWidget/CheckboxesWidget.tsx
@@ -1,6 +1,11 @@
import React from "react";
import Form from "react-bootstrap/Form";
import { WidgetProps } from "@rjsf/utils";
import {
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from "@rjsf/utils";

const selectValue = (value: any, selected: any, all: any) => {
const at = all.indexOf(value);
Expand All @@ -15,7 +20,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
>({
id,
disabled,
options,
Expand All @@ -26,7 +35,7 @@ const CheckboxesWidget = ({
onChange,
onBlur,
onFocus,
}: WidgetProps) => {
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, inline } = options;

const _onChange =
Expand Down Expand Up @@ -78,6 +87,4 @@ const CheckboxesWidget = ({
})}
</Form.Group>
);
};

export default CheckboxesWidget;
}
17 changes: 12 additions & 5 deletions packages/bootstrap-4/src/DescriptionField/DescriptionField.tsx
@@ -1,7 +1,16 @@
import React from "react";
import { DescriptionFieldProps } from "@rjsf/utils";
import {
DescriptionFieldProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const DescriptionField = ({ id, description }: DescriptionFieldProps) => {
export default function DescriptionField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({ id, description }: DescriptionFieldProps<T, S, F>) {
if (description) {
return (
<div>
Expand All @@ -13,6 +22,4 @@ const DescriptionField = ({ id, description }: DescriptionFieldProps) => {
}

return null;
};

export default DescriptionField;
}
47 changes: 28 additions & 19 deletions packages/bootstrap-4/src/ErrorList/ErrorList.tsx
Expand Up @@ -3,23 +3,32 @@ import React from "react";
import Card from "react-bootstrap/Card";
import ListGroup from "react-bootstrap/ListGroup";

import { ErrorListProps } from "@rjsf/utils";
import {
ErrorListProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from "@rjsf/utils";

const ErrorList = ({ errors }: ErrorListProps) => (
<Card border="danger" className="mb-4">
<Card.Header className="alert-danger">Errors</Card.Header>
<Card.Body className="p-0">
<ListGroup>
{errors.map((error, i: number) => {
return (
<ListGroup.Item key={i} className="border-0">
<span>{error.stack}</span>
</ListGroup.Item>
);
})}
</ListGroup>
</Card.Body>
</Card>
);

export default ErrorList;
export default function ErrorList<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({ errors }: ErrorListProps<T, S, F>) {
return (
<Card border="danger" className="mb-4">
<Card.Header className="alert-danger">Errors</Card.Header>
<Card.Body className="p-0">
<ListGroup>
{errors.map((error, i: number) => {
return (
<ListGroup.Item key={i} className="border-0">
<span>{error.stack}</span>
</ListGroup.Item>
);
})}
</ListGroup>
</Card.Body>
</Card>
);
}
13 changes: 11 additions & 2 deletions packages/bootstrap-4/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 ListGroup from "react-bootstrap/ListGroup";

/** 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<T, S, F>) {
const { errors = [], idSchema } = props;
if (errors.length === 0) {
return null;
Expand Down

0 comments on commit 51a6517

Please sign in to comment.