Skip to content

Commit

Permalink
[fix] Fix custom validation playground example (#3858)
Browse files Browse the repository at this point in the history
* [fix] #3856
- update `validation` example to use RJSF v5 API
- add types for all samples and fix/ignore uncovered type issues
- Spread sample data as extra form props

* Changes from code review
- Use ErrorSchemaBuilder
- Deep freeze playground samples
- Remove non-latin1 apostrophes

---------

Co-authored-by: Heath C <51679588+heath-freenome@users.noreply.github.com>
  • Loading branch information
nickgros and heath-freenome committed Oct 5, 2023
1 parent 208b148 commit e487116
Show file tree
Hide file tree
Showing 43 changed files with 249 additions and 109 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ return (<StyleProvider><YourFormComponents /></StyleProvider>);

- Fixed a faulty check of the `isMultiple` option in `MultiSchemaField`. It no longer offers multiple choice inside a select field in a `oneOf` case in Chakra UI, fixing [#3848](https://github.com/rjsf-team/react-jsonschema-form/issues/3848)

## Dev / docs / playground

- Fixed custom validation playground example ([#3856](https://github.com/rjsf-team/react-jsonschema-form/issues/3856))

# 5.12.1

## @rjsf/validator-ajv8
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 16 additions & 25 deletions packages/docs/docs/usage/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,11 @@ const validator = createPrecompiledValidator(precompiledValidator as ValidatorFu

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

### Dynamically pre-compiling validators

For more advanced cases when schema needs to be precompiled on request - `compileSchemaValidatorsCode` can be used.

```ts
import { compileSchemaValidatorsCode } from '@rjsf/validator-ajv8/dist/compileSchemaValidators';

Expand All @@ -103,7 +105,7 @@ For the most part it is the same as `compileSchemaValidators`, but instead of wr

To use it on browser side - some modifications are needed to provide runtime dependencies in generated code needs to be provided.

Example implementation of it:
Example implementation of it:

```tsx
import type { ValidatorFunctions } from '@rjsf/validator-ajv8';
Expand All @@ -121,19 +123,13 @@ import ajvRuntimeUcs2length from 'ajv/dist/runtime/ucs2length';
import ajvRuntimeUri from 'ajv/dist/runtime/uri';
import * as ajvFormats from 'ajv-formats/dist/formats';

// dependencies to replace in generated code, to be provided by at runtime
// dependencies to replace in generated code, to be provided by at runtime
const validatorsBundleReplacements: Record<string, [string, unknown]> = {
// '<code to be replaced>': ['<variable name to use as replacement>', <runtime dependency>],
// '<code to be replaced>': ['<variable name to use as replacement>', <runtime dependency>],
'require("ajv/dist/runtime/equal").default': ['ajvRuntimeEqual', ajvRuntimeEqual],
'require("ajv/dist/runtime/parseJson").parseJson': ['ajvRuntimeparseJson', ajvRuntimeparseJson],
'require("ajv/dist/runtime/parseJson").parseJsonNumber': [
'ajvRuntimeparseJsonNumber',
ajvRuntimeparseJsonNumber,
],
'require("ajv/dist/runtime/parseJson").parseJsonString': [
'ajvRuntimeparseJsonString',
ajvRuntimeparseJsonString,
],
'require("ajv/dist/runtime/parseJson").parseJsonNumber': ['ajvRuntimeparseJsonNumber', ajvRuntimeparseJsonNumber],
'require("ajv/dist/runtime/parseJson").parseJsonString': ['ajvRuntimeparseJsonString', ajvRuntimeparseJsonString],
'require("ajv/dist/runtime/quote").default': ['ajvRuntimeQuote', ajvRuntimeQuote],
// re2 by default is not in dependencies for ajv and so is likely not normally used
// 'require("ajv/dist/runtime/re2").default': ['ajvRuntimeRe2', ajvRuntimeRe2],
Expand Down Expand Up @@ -167,10 +163,7 @@ const schemas = new Map<
>();
if (typeof window !== 'undefined') {
// @ts-ignore
window[windowValidatorOnLoad] = (
loadedId: string,
fn: (...args: unknown[]) => ValidatorFunctions
) => {
window[windowValidatorOnLoad] = (loadedId: string, fn: (...args: unknown[]) => ValidatorFunctions) => {
const validator = fn(...Object.values(validatorsBundleReplacements).map(([, dep]) => dep));
let validatorLoader = schemas.get(loadedId);
if (validatorLoader) {
Expand Down Expand Up @@ -207,28 +200,26 @@ export function evaluateValidator(id: string, code: string, nonce: string): Prom
document.body.appendChild(scriptElement);
return validatorPromise;
}

```

From React component this can be used as following:
From React component this can be used as following:

```tsx
let [precompiledValidator, setPrecompiledValidator] = React.useState<ValidatorFunctions>();
React.useEffect(() => {
evaluateValidator(
schemaId, // some schema id to avoid evaluating it multiple times
code, // result of compileSchemaValidatorsCode returned from the server
nonce // nonce script tag attribute to allow this ib content security policy for the page
).then(setPrecompiledValidator);
evaluateValidator(
schemaId, // some schema id to avoid evaluating it multiple times
code, // result of compileSchemaValidatorsCode returned from the server
nonce // nonce script tag attribute to allow this ib content security policy for the page
).then(setPrecompiledValidator);
}, [entityType.id]);

if (!precompiledValidator) {
// render loading screen
// render loading screen
}
const validator = createPrecompiledValidator(precompiledValidator, schema);
```


## Live validation

By default, form data are only validated when the form is submitted or when a new `formData` prop is passed to the `Form` component.
Expand Down Expand Up @@ -526,7 +517,7 @@ const validator = customizeValidator({ customFormats });
render(<Form schema={schema} validator={validator} />, document.getElementById('app'));
```

Format values can be anything AJVs [`addFormat` method](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#addformatstring-name-stringregexpfunctionobject-format---ajv) accepts.
Format values can be anything AJV's [`addFormat` method](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#addformatstring-name-stringregexpfunctionobject-format---ajv) accepts.

### Async validation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ render((
), document.getElementById("app"));
```

Format values can be anything AJVs [`addFormat` method](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#addformatstring-name-stringregexpfunctionobject-format---ajv) accepts.
Format values can be anything AJV's [`addFormat` method](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#addformatstring-name-stringregexpfunctionobject-format---ajv) accepts.

## Async validation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ render((
), document.getElementById("app"));
```

Format values can be anything AJVs [`addFormat` method](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#addformatstring-name-stringregexpfunctionobject-format---ajv) accepts.
Format values can be anything AJV's [`addFormat` method](https://github.com/ajv-validator/ajv/tree/6a671057ea6aae690b5967ee26a0ddf8452c6297#addformatstring-name-stringregexpfunctionobject-format---ajv) accepts.

## Async validation

Expand Down
1 change: 1 addition & 0 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"chakra-react-select": "^4.7.0",
"core-js": "^3.32.2",
"dayjs": "^1.11.9",
"deep-freeze-es6": "^1.4.1",
"framer-motion": "^5.6.0",
"jss": "^10.10.0",
"lodash": "^4.17.21",
Expand Down
34 changes: 22 additions & 12 deletions packages/playground/src/components/Playground.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useState, useRef, useEffect, ComponentType, FormEvent } from 'react';
import { withTheme, IChangeEvent, FormProps } from '@rjsf/core';
import { ErrorSchema, RJSFSchema, RJSFValidationError, TemplatesType, UiSchema, ValidatorType } from '@rjsf/utils';
import { ComponentType, FormEvent, useCallback, useEffect, useRef, useState } from 'react';
import { FormProps, IChangeEvent, withTheme } from '@rjsf/core';
import { ErrorSchema, RJSFSchema, RJSFValidationError, UiSchema, ValidatorType } from '@rjsf/utils';

import { samples } from '../samples';
import Header, { LiveSettings } from './Header';
Expand All @@ -10,6 +10,7 @@ import GeoPosition from './GeoPosition';
import { ThemesType } from './ThemeSelector';
import Editors from './Editors';
import SpecialInput from './SpecialInput';
import { Sample } from '../samples/Sample';

export interface PlaygroundProps {
themes: { [themeName: string]: ThemesType };
Expand Down Expand Up @@ -39,7 +40,7 @@ export default function Playground({ themes, validators }: PlaygroundProps) {
experimental_defaultFormStateBehavior: { arrayMinItems: 'populate', emptyObjectFields: 'populateAllDefaults' },
});
const [FormComponent, setFormComponent] = useState<ComponentType<FormProps>>(withTheme({}));
const [templates, setTemplates] = useState<Partial<TemplatesType>>();
const [otherFormProps, setOtherFormProps] = useState<Partial<FormProps>>({});

const playGroundFormRef = useRef<any>(null);

Expand All @@ -54,12 +55,21 @@ export default function Playground({ themes, validators }: PlaygroundProps) {
);

const load = useCallback(
(data: any) => {
// Reset the ArrayFieldTemplate whenever you load new data
const { templates = {}, extraErrors, liveSettings } = data;
// uiSchema is missing on some examples. Provide a default to
// clear the field in all cases.
const { schema, uiSchema = {}, formData, theme: dataTheme = theme } = data;
(data: Sample & { theme: string; liveSettings: LiveSettings }) => {
const {
schema,
// uiSchema is missing on some examples. Provide a default to
// clear the field in all cases.
uiSchema = {},
// Always reset templates and fields
templates = {},
fields = {},
formData,
theme: dataTheme = theme,
extraErrors,
liveSettings,
...rest
} = data;

onThemeSelected(dataTheme, themes[dataTheme]);

Expand All @@ -70,9 +80,9 @@ export default function Playground({ themes, validators }: PlaygroundProps) {
setFormData(formData);
setExtraErrors(extraErrors);
setTheme(dataTheme);
setTemplates(templates);
setShowForm(true);
setLiveSettings(liveSettings);
setOtherFormProps({ fields, templates, ...rest });
},
[theme, onThemeSelected, themes]
);
Expand Down Expand Up @@ -166,8 +176,8 @@ export default function Playground({ themes, validators }: PlaygroundProps) {
theme={theme}
>
<FormComponent
{...otherFormProps}
{...liveSettings}
templates={templates}
extraErrors={extraErrors}
schema={schema}
uiSchema={uiSchema}
Expand Down
3 changes: 3 additions & 0 deletions packages/playground/src/samples/Sample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { FormProps } from '@rjsf/core';

export type Sample = Omit<FormProps, 'validator'>;
6 changes: 5 additions & 1 deletion packages/playground/src/samples/additionalProperties.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default {
import { Sample } from './Sample';

const additionalProperties: Sample = {
schema: {
title: 'A customizable registration form',
description: 'A simple form with additional properties example.',
Expand Down Expand Up @@ -30,3 +32,5 @@ export default {
assKickCount: 'infinity',
},
};

export default additionalProperties;
6 changes: 5 additions & 1 deletion packages/playground/src/samples/allOf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default {
import { Sample } from './Sample';

const allOf: Sample = {
schema: {
type: 'object',
allOf: [
Expand All @@ -24,3 +26,5 @@ export default {
},
formData: {},
};

export default allOf;
6 changes: 5 additions & 1 deletion packages/playground/src/samples/alternatives.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default {
import { Sample } from './Sample';

const alternatives: Sample = {
schema: {
definitions: {
Color: {
Expand Down Expand Up @@ -90,3 +92,5 @@ export default {
blendMode: 'screen',
},
};

export default alternatives;
6 changes: 5 additions & 1 deletion packages/playground/src/samples/anyOf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default {
import { Sample } from './Sample';

const anyOf: Sample = {
schema: {
type: 'object',
properties: {
Expand Down Expand Up @@ -57,3 +59,5 @@ export default {
},
formData: {},
};

export default anyOf;
6 changes: 5 additions & 1 deletion packages/playground/src/samples/arrays.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default {
import { Sample } from './Sample';

const arrays: Sample = {
schema: {
definitions: {
Thing: {
Expand Down Expand Up @@ -189,3 +191,5 @@ export default {
fixedNoToolbar: [42, true, 'additional item one', 'additional item two'],
},
};

export default arrays;
6 changes: 5 additions & 1 deletion packages/playground/src/samples/custom.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default {
import { Sample } from './Sample';

const custom: Sample = {
schema: {
title: 'A localisation form',
type: 'object',
Expand All @@ -20,3 +22,5 @@ export default {
lon: 0,
},
};

export default custom;
17 changes: 9 additions & 8 deletions packages/playground/src/samples/customArray.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
const ArrayFieldTemplate: React.FC<{ className: string; items?: any[]; canAdd?: boolean; onAddClick: () => void }> = ({
className,
items,
canAdd,
onAddClick,
}) => {
import { Sample } from './Sample';
import { ArrayFieldTemplateProps } from '@rjsf/utils';

function ArrayFieldTemplate(props: ArrayFieldTemplateProps) {
const { className, items, canAdd, onAddClick } = props;
return (
<div className={className}>
{items &&
Expand Down Expand Up @@ -32,9 +31,9 @@ const ArrayFieldTemplate: React.FC<{ className: string; items?: any[]; canAdd?:
)}
</div>
);
};
}

export default {
export const customArray: Sample = {
schema: {
title: 'Custom array of strings',
type: 'array',
Expand All @@ -45,3 +44,5 @@ export default {
formData: ['react', 'jsonschema', 'form'],
templates: { ArrayFieldTemplate },
};

export default customArray;
6 changes: 5 additions & 1 deletion packages/playground/src/samples/customField.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default {
import { Sample } from './Sample';

const customField: Sample = {
schema: {
title: 'A registration form',
description: 'A custom-field form example.',
Expand Down Expand Up @@ -26,3 +28,5 @@ export default {
mySpecialStringField: 'special-text',
},
};

export default customField;
Loading

0 comments on commit e487116

Please sign in to comment.