Skip to content

Commit

Permalink
fix: Add typescript documentation to advanced customization
Browse files Browse the repository at this point in the history
partially fix: rjsf-team#3072

- Fixed a broken example in `custom-templates.md`
- Fixed broken links in the `form-props.md`
- Added a new `typescript.md` file to `advanced-customization` to document using custom generics
- Updated `mkdocs.yml` to replacing `Contributing` with the new `typescript.md` file under `Advanced Customization`, moving `Contributing` from the bottom to be after `Quickstart`
  - Also moved all of the `Migration Guides` into its own subsection
- Updated the `CHANGELOG.md` file accordingly
  • Loading branch information
heath-freenome committed Jan 7, 2023
1 parent 08cb1b2 commit 8ebfbe4
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 13 deletions.
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

0 comments on commit 8ebfbe4

Please sign in to comment.