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 typescript documentation to advanced customization #3355

Merged
merged 2 commits into from Jan 8, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -67,7 +67,9 @@ should change the heading of the (upcoming) version to include a major version b
- Updated the `ArrayFieldTemplateItemType` to add the new `totalItems` and `canAdd` props, fixing [#3315](https://github.com/rjsf-team/react-jsonschema-form/issues/3315)

## Dev / docs / playground
- Fixed the documentation for `ArrayFieldItemTemplate`, `SubmitButtonProps` and `IconButtonProps` as part of the fix for [#3314](https://github.com/rjsf-team/react-jsonschema-form/issues/3314) and [#3315](https://github.com/rjsf-team/react-jsonschema-form/issues/3315)
- Fixed the documentation for `ArrayFieldItemTemplate`, `SubmitButtonProps` and `IconButtonProps` as part of the fix for [#3314](https://github.com/rjsf-team/react-jsonschema-form/issues/3314) and [#3315](https://github.com/rjsf-team/react-jsonschema-form/issues/3315)
- Updated the documentation in `form-props.md` for `children`, fixing [#3322](https://github.com/rjsf-team/react-jsonschema-form/issues/3322)
- Added new `typescript.md` documentation to `Advanced Customization` describing how to use custom generics as part of the fix for [#3072](https://github.com/rjsf-team/react-jsonschema-form/issues/3072)

# 5.0.0-beta-15

Expand Down
9 changes: 4 additions & 5 deletions docs/advanced-customization/custom-templates.md
Expand Up @@ -811,11 +811,10 @@ function WrapIfAdditionalTemplate(
onBlur={function (event) { onKeyChange(event.target.value) } }
defaultValue={label} />
<div>{children}</div>
<RemoveButton
onClick={onDropPropertyClick(label)}
uiSchema={uiSchema}
/>
</div>
<RemoveButton
onClick={onDropPropertyClick(label)}
uiSchema={uiSchema}
/>
</div>
);
}
Expand Down
273 changes: 273 additions & 0 deletions docs/advanced-customization/typescript.md
@@ -0,0 +1,273 @@
# Typescript Support
RJSF fully supports Typescript.
The [types and functions](../api-reference/utility-functions.md) exported by `@rjsf/utils` are fully typed (as needed) using one or more of the following 3 optional generics:

- `T = any`: This represents the type of the `formData` and defaults to `any`.
- `S extends StrictRJSFSchema = RJSFSchema`: This represents the type of the `schema` and extends the `StrictRJSFSchema` type and defaults to the `RJSFSchema` type.
- `F extends FormContextType = any`: This represents the type of the `formContext`, extends the `FormContextType` type and defaults to `any`.

Every other library in the `@rjsf/*` ecosystem use these same generics in their functions and React component definitions.
For instance, in the `@rjsf/core` library the definitions of the `Form` component and the `withTheme()` and `getDefaultRegistry()` functions are as follows:

```ts
export default class Form<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
> extends Component<FormProps<T, S, F>, FormState<T, S, F>> {
// ... class implementation
}

export default function withTheme<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(themeProps: ThemeProps<T, S, F>) {
// ... function implementation
}

export default function getDefaultRegistry<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(): Omit<Registry<T, S, F>, "schemaUtils"> {
// ... function implementation
}
```

Out of the box, the defaults for these generics will work for all use-cases.
Providing custom types for any of these generics may be useful for situations where the caller is working with typed `formData`, `schema` or `formContext` props, Typescript is complaining and type casting isn't allowed.

## Overriding generics

### T

The generic `T` is used to represent the type of the `formData` property passed into `Form`.
If you are working with a simple, unchanging JSON Schema and you have defined a type for the `formData` you are working with, you can override this generic as follows:

```tsx
import { RJSFSchema } from "@rjsf/utils";
import { validator } from "@rjsf/validator-ajv8";
import { Form } from "@rjsf/core";

interface FormData {
foo?: string;
bar?: number;
};

const schema: RJSFSchema = {
type: "object",
properties: {
"foo": { type: "string" },
"bar": { type: "number" }
}
};

const formData: FormData = {};

render((
<Form<FormData> schema={schema} validator={validator} formData={formData} />
), document.getElementById("app"));
```

### S

The generic `S` is used to represent the type of the `schema` property passed into `Form`.
If you are using something like the [Ajv utility types for schemas](https://ajv.js.org/guide/typescript.html#utility-types-for-schemas) typing system, you can override this generic as follows:

```tsx
import { JSONSchemaType } from "ajv";
import { RJSFSchema } from "@rjsf/utils";
import { validator } from "@rjsf/validator-ajv8";
import { Form } from "@rjsf/core";

interface FormData {
foo?: string;
bar?: number;
};

type MySchema = JSONSchemaType<FormData>;

const schema: MySchema = {
type: "object",
properties: {
"foo": { type: "string" },
"bar": { type: "number" }
}
};

render((
<Form<any, MySchema> schema={schema} validator={validator} />
), document.getElementById("app"));

// Alternatively since you have the type, you could also use this
// render((
// <Form<FormData, MySchema> schema={schema} validator={validator} />
//), document.getElementById("app"));
```

> NOTE: using this `Ajv typing system` has not been tested extensively with RJSF, so use carefully

### F

The generic `F` is used to represent the type of the `formContext` property passed into `Form`.
If you have a type for this data, you can override this generic as follows:

```tsx
import { RJSFSchema } from "@rjsf/utils";
import { validator } from "@rjsf/validator-ajv8";
import { Form } from "@rjsf/core";

interface FormContext {
myCustomWidgetData: object;
};

const schema: RJSFSchema = {
type: "object",
properties: {
"foo": { type: "string" },
"bar": { type: "number" }
}
};

const formContext: FormContext = {
myCustomWidgetData: {
enableCustomFeature: true,
}
};

render((
<Form<any, RJSFSchema, FormContext> schema={schema} validator={validator} formContext={formContext} />
), document.getElementById("app"));
```

## Overriding generics in core
As shown in previous examples, overriding the default `Form` from `@rjsf/core` is pretty straight forward.
Using the `withTheme()` function is just as easy:

```tsx
import { RJSFSchema } from "@rjsf/utils";
import { validator } from "@rjsf/validator-ajv8";
import { withTheme, ThemeProps } from '@rjsf/core';

interface FormData {
foo?: string;
bar?: number;
};

type MySchema = JSONSchemaType<FormData>;

const schema: MySchema = {
type: "object",
properties: {
"foo": { type: "string" },
"bar": { type: "number" }
}
};

interface FormContext {
myCustomWidgetData: object;
};

const theme: ThemeProps<FormData, MySchema, FormContext> = { widgets: {test: () => (<div>test</div>) } };

const ThemedForm = withTheme<FormData, MySchema, FormContext>(theme);

const Demo = () => (
<ThemedForm schema={schema} uiSchema={uiSchema} validator={validator} />
);
```

## Overriding generics in other themes
Since all the other themes in RJSF are extensions of `@rjsf/core`, overriding parts of these themes with custom generics is a little different.
The exported `Theme` and `Form` from any of the themes have been created using the generic defaults, and as a result, do not take generics themselves.
In order to override generics, special `generateForm()` and `generateTheme()` functions are exported for your use.

### Overriding a Theme
If you are doing something like the following to create a new theme based on `@rjsf/mui` to extend one or more `templates`:

```tsx
import React from "react";
import { WidgetProps } from "@rjsf/utils";
import { ThemeProps, withTheme } from "@rjsf/core";
import { validator } from "@rjsf/validator-ajv8";
import { Theme } from "@rjsf/mui";

const OldBaseInputTemplate = Theme.templates.BaseInputTemplate;

// Force the underlying `TextField` component to always use size="small"
function MyBaseInputTemplate(props: WidgetProps) {
return <OldBaseInputTemplate {...props} size="small" />;
}

const myTheme: ThemeProps = {
...Theme,
templates: {
...Theme.templates,
BaseInputTemplate: MyBaseInputTemplate,
},
};

const ThemedForm = withTheme(myTheme);

const Demo = () => (
<ThemedForm schema={schema} uiSchema={uiSchema} validator={validator} />
);
```

Then you would use the new `generateTheme()` and `generateForm()` functions as follows:

```tsx
import React from "react";
import { WidgetProps } from "@rjsf/utils";
import { ThemeProps, withTheme } from "@rjsf/core";
import { validator } from "@rjsf/validator-ajv8";
import { generateTheme } from "@rjsf/mui";

interface FormData {
foo?: string;
bar?: number;
};

type MySchema = JSONSchemaType<FormData>;

const schema: MySchema = {
type: "object",
properties: {
"foo": { type: "string" },
"bar": { type: "number" }
}
};

interface FormContext {
myCustomWidgetData: object;
};

const Theme: ThemeProps<FormData, MySchema, FormContext> = generateTheme<FormData, MySchema, FormContext>();

const OldBaseInputTemplate = Theme.templates.BaseInputTemplate;

// Force the underlying `TextField` component to always use size="small"
function MyBaseInputTemplate(props: WidgetProps<FormData, MySchema, FormContext>) {
return <OldBaseInputTemplate {...props} size="small" />;
}

const myTheme: ThemeProps<FormData, MySchema, FormContext> = {
...Theme,
templates: {
...Theme.templates,
BaseInputTemplate: MyBaseInputTemplate,
},
};

const ThemedForm = withTheme<FormData, MySchema, FormContext>(myTheme);

// You could also do since they are effectively the same:
// const ThemedForm = generateForm<FormData, MySchema, FormContext>(myTheme);

const Demo = () => (
<ThemedForm schema={schema} uiSchema={uiSchema} validator={validator} />
);
```

> NOTE: The same approach works for extending `widgets` and `fields` as well.
2 changes: 1 addition & 1 deletion docs/api-reference/form-props.md
Expand Up @@ -26,7 +26,7 @@ The value of this prop will be passed to the `class` [HTML attribute on the form

You can provide custom buttons to your form via the `Form` component's `children`. If no children are provided, by default a `Submit` button will be rendered.

For other ways to modify the default `Submit` button, see both the [Submit Button Options](./uiSchema/#submitbuttonoptions) and the [SubmitButton Template](../advanced-customization/custom-templates/#submitbutton) documentation.
For other ways to modify the default `Submit` button, see both the [Submit Button Options](../uiSchema.md#submitbuttonoptions) and the [SubmitButton Template](../../advanced-customization/custom-templates.md#submitbutton) documentation.

```tsx
import { RJSFSchema } from "@rjsf/utils";
Expand Down
13 changes: 7 additions & 6 deletions mkdocs.yml
Expand Up @@ -7,6 +7,7 @@ extra_javascript: [main.js]
nav:
- Introduction: index.md
- Quickstart: quickstart.md
- Contributing: contributing.md
- JSON schema:
- Single fields: usage/single.md
- Objects: usage/objects.md
Expand All @@ -23,18 +24,18 @@ nav:
- Custom Templates: advanced-customization/custom-templates.md
- Custom Themes: advanced-customization/custom-themes.md
- Other Internals: advanced-customization/internals.md
- Contributing: contributing.md
- Typescript Support: advanced-customization/typescript.md
- API Reference:
- General uiSchema Reference: api-reference/uiSchema.md
- semantic-ui uiSchema Reference: api-reference/themes/semantic-ui/uiSchema.md
- chakra-ui uiSchema Reference: api-reference/themes/chakra-ui/uiSchema.md
- "&lt;Form /&gt; props": api-reference/form-props.md
- Utility functions: api-reference/utility-functions.md
- 5.x Migration: 5.x upgrade guide.md
- 4.x Migration: 4.x upgrade guide.md
- 3.x Migration: 3.x upgrade guide.md
- 2.x Migration: 2.x upgrade guide.md
- Contributing: contributing.md
- Migration Guides:
- 5.x Migration: 5.x upgrade guide.md
- 4.x Migration: 4.x upgrade guide.md
- 3.x Migration: 3.x upgrade guide.md
- 2.x Migration: 2.x upgrade guide.md

markdown_extensions:
- toc:
Expand Down