updated rjsf to beta.16 and updated validations from v6 to v8 w/ burnettk

This commit is contained in:
jasquat 2023-01-10 15:15:21 -05:00
parent 9eb705ca76
commit bac203727e
8 changed files with 328 additions and 258 deletions

View File

@ -23,7 +23,7 @@
"@rjsf/core": "*", "@rjsf/core": "*",
"@rjsf/mui": "^5.0.0-beta.13", "@rjsf/mui": "^5.0.0-beta.13",
"@rjsf/utils": "^5.0.0-beta.13", "@rjsf/utils": "^5.0.0-beta.13",
"@rjsf/validator-ajv6": "^5.0.0-beta.13", "@rjsf/validator-ajv8": "^5.0.0-beta.16",
"@tanstack/react-table": "^8.2.2", "@tanstack/react-table": "^8.2.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0", "@testing-library/react": "^13.3.0",
@ -4863,9 +4863,9 @@
} }
}, },
"node_modules/@rjsf/core": { "node_modules/@rjsf/core": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.0.0-beta.16.tgz",
"integrity": "sha512-uQ3A9aJhMJsz9ct5tV3ogZkSFEkKUxrM9SJ9Hc8ijxmuaW7Jv8tNv5jiWZZsLvNXlIONX83s6JqkiOJf6IOAvg==", "integrity": "sha512-TqOd3CKptWAswX9PU8pLSoAe5zI03J6Kk/aWAFbMj+xW/6hR5PXHbs5X5kxwpQx7IVXiJZZZpP5n1oDsu4GwNg==",
"dependencies": { "dependencies": {
"lodash": "^4.17.15", "lodash": "^4.17.15",
"lodash-es": "^4.17.15", "lodash-es": "^4.17.15",
@ -4881,9 +4881,9 @@
} }
}, },
"node_modules/@rjsf/mui": { "node_modules/@rjsf/mui": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.0.0-beta.16.tgz",
"integrity": "sha512-hwCtADpjNssq/CsT3Wj1FDVJfdCN3gptKedGjbusLUEwQqXoVzkzl25e/IRfN8y/JxYu4lMXDU89bN9nJSKWLA==", "integrity": "sha512-QskaSc2Zcwqz+nKoACstvn5LhrAx4EmicYc/6kNoj3jKH6MlfVCA7FYumu5g6TIqMrDEvZuZqBKtjL64Tv52PQ==",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
}, },
@ -4898,9 +4898,9 @@
} }
}, },
"node_modules/@rjsf/utils": { "node_modules/@rjsf/utils": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.0.0-beta.16.tgz",
"integrity": "sha512-hWWWFD2ifjSOhqWueML4OHrZe2HW5pE2nfKGhCObFbwtggHoQlj64xDBsJ1qfUG8DGvCHztJQ/sKIaOvXnpt7w==", "integrity": "sha512-dNQ620Q6a9cB28sjjRgJkxIuD9TFd03sNMlcZVdZOuZC6wjfGc4rKG0Lc7+xgLFvSPFKwXJprzfKSM3yuy9jXg==",
"dependencies": { "dependencies": {
"json-schema-merge-allof": "^0.8.1", "json-schema-merge-allof": "^0.8.1",
"jsonpointer": "^5.0.1", "jsonpointer": "^5.0.1",
@ -4933,12 +4933,13 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
}, },
"node_modules/@rjsf/validator-ajv6": { "node_modules/@rjsf/validator-ajv8": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv6/-/validator-ajv6-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.0.0-beta.16.tgz",
"integrity": "sha512-X9N3/HJYV23MjUN/VJHIdBhUdBuMTUsh4HAZm50eUvUAhWK95wIqjjhAs24rzeLajrjFeH7kFr89zAqDgIFhVQ==", "integrity": "sha512-VrQzR9HEH/1BF2TW/lRJuV+kILzR4geS+iW5Th1OlPeNp1NNWZuSO1kCU9O0JA17t2WHOEl/SFZXZBnN1/zwzQ==",
"dependencies": { "dependencies": {
"ajv": "^6.7.0", "ajv-formats": "^2.1.1",
"ajv8": "npm:ajv@^8.11.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"lodash-es": "^4.17.15" "lodash-es": "^4.17.15"
}, },
@ -4946,7 +4947,7 @@
"node": ">=14" "node": ">=14"
}, },
"peerDependencies": { "peerDependencies": {
"@rjsf/utils": "^5.0.0-beta.1" "@rjsf/utils": "^5.0.0-beta.12"
} }
}, },
"node_modules/@rollup/plugin-babel": { "node_modules/@rollup/plugin-babel": {
@ -6822,6 +6823,27 @@
"ajv": "^6.9.1" "ajv": "^6.9.1"
} }
}, },
"node_modules/ajv8": {
"name": "ajv",
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv8/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/ansi-align": { "node_modules/ansi-align": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
@ -34767,9 +34789,9 @@
} }
}, },
"@rjsf/core": { "@rjsf/core": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.0.0-beta.16.tgz",
"integrity": "sha512-uQ3A9aJhMJsz9ct5tV3ogZkSFEkKUxrM9SJ9Hc8ijxmuaW7Jv8tNv5jiWZZsLvNXlIONX83s6JqkiOJf6IOAvg==", "integrity": "sha512-TqOd3CKptWAswX9PU8pLSoAe5zI03J6Kk/aWAFbMj+xW/6hR5PXHbs5X5kxwpQx7IVXiJZZZpP5n1oDsu4GwNg==",
"requires": { "requires": {
"lodash": "^4.17.15", "lodash": "^4.17.15",
"lodash-es": "^4.17.15", "lodash-es": "^4.17.15",
@ -34778,15 +34800,15 @@
} }
}, },
"@rjsf/mui": { "@rjsf/mui": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/mui/-/mui-5.0.0-beta.16.tgz",
"integrity": "sha512-hwCtADpjNssq/CsT3Wj1FDVJfdCN3gptKedGjbusLUEwQqXoVzkzl25e/IRfN8y/JxYu4lMXDU89bN9nJSKWLA==", "integrity": "sha512-QskaSc2Zcwqz+nKoACstvn5LhrAx4EmicYc/6kNoj3jKH6MlfVCA7FYumu5g6TIqMrDEvZuZqBKtjL64Tv52PQ==",
"requires": {} "requires": {}
}, },
"@rjsf/utils": { "@rjsf/utils": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.0.0-beta.16.tgz",
"integrity": "sha512-hWWWFD2ifjSOhqWueML4OHrZe2HW5pE2nfKGhCObFbwtggHoQlj64xDBsJ1qfUG8DGvCHztJQ/sKIaOvXnpt7w==", "integrity": "sha512-dNQ620Q6a9cB28sjjRgJkxIuD9TFd03sNMlcZVdZOuZC6wjfGc4rKG0Lc7+xgLFvSPFKwXJprzfKSM3yuy9jXg==",
"requires": { "requires": {
"json-schema-merge-allof": "^0.8.1", "json-schema-merge-allof": "^0.8.1",
"jsonpointer": "^5.0.1", "jsonpointer": "^5.0.1",
@ -34812,12 +34834,13 @@
} }
} }
}, },
"@rjsf/validator-ajv6": { "@rjsf/validator-ajv8": {
"version": "5.0.0-beta.13", "version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv6/-/validator-ajv6-5.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.0.0-beta.16.tgz",
"integrity": "sha512-X9N3/HJYV23MjUN/VJHIdBhUdBuMTUsh4HAZm50eUvUAhWK95wIqjjhAs24rzeLajrjFeH7kFr89zAqDgIFhVQ==", "integrity": "sha512-VrQzR9HEH/1BF2TW/lRJuV+kILzR4geS+iW5Th1OlPeNp1NNWZuSO1kCU9O0JA17t2WHOEl/SFZXZBnN1/zwzQ==",
"requires": { "requires": {
"ajv": "^6.7.0", "ajv-formats": "^2.1.1",
"ajv8": "npm:ajv@^8.11.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"lodash-es": "^4.17.15" "lodash-es": "^4.17.15"
} }
@ -36295,6 +36318,24 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"requires": {} "requires": {}
}, },
"ajv8": {
"version": "npm:ajv@8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"dependencies": {
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
}
}
},
"ansi-align": { "ansi-align": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",

View File

@ -18,7 +18,7 @@
"@rjsf/core": "*", "@rjsf/core": "*",
"@rjsf/mui": "^5.0.0-beta.13", "@rjsf/mui": "^5.0.0-beta.13",
"@rjsf/utils": "^5.0.0-beta.13", "@rjsf/utils": "^5.0.0-beta.13",
"@rjsf/validator-ajv6": "^5.0.0-beta.13", "@rjsf/validator-ajv8": "^5.0.0-beta.16",
"@tanstack/react-table": "^8.2.2", "@tanstack/react-table": "^8.2.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0", "@testing-library/react": "^13.3.0",

View File

@ -1,13 +1,6 @@
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import validator from '@rjsf/validator-ajv8';
// FIXME: npm install @rjsf/validator-ajv8 and use it as soon as
// rawErrors is fixed.
// https://react-jsonschema-form.readthedocs.io/en/latest/usage/validation/
// https://github.com/rjsf-team/react-jsonschema-form/issues/2309 links to a codesandbox that might be useful to fork
// if we wanted to file a defect against rjsf to show the difference between validator-ajv6 and validator-ajv8.
// https://github.com/rjsf-team/react-jsonschema-form/blob/main/docs/api-reference/uiSchema.md talks about rawErrors
import validator from '@rjsf/validator-ajv6';
import { import {
TabList, TabList,

View File

@ -1,12 +1,23 @@
import React, { CSSProperties } from 'react'; import React, { CSSProperties } from 'react';
import Box from '@mui/material/Box'; import {
import Grid from '@mui/material/Grid'; ArrayFieldTemplateItemType,
import Paper from '@mui/material/Paper'; FormContextType,
import { ArrayFieldTemplateItemType } from '@rjsf/utils'; RJSFSchema,
StrictRJSFSchema,
} from '@rjsf/utils';
function ArrayFieldItemTemplate(props: 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 { const {
children, children,
className,
disabled, disabled,
hasToolbar, hasToolbar,
hasMoveDown, hasMoveDown,
@ -16,8 +27,8 @@ function ArrayFieldItemTemplate(props: ArrayFieldTemplateItemType) {
onDropIndexClick, onDropIndexClick,
onReorderClick, onReorderClick,
readonly, readonly,
uiSchema,
registry, registry,
uiSchema,
} = props; } = props;
const { MoveDownButton, MoveUpButton, RemoveButton } = const { MoveDownButton, MoveUpButton, RemoveButton } =
registry.templates.ButtonTemplates; registry.templates.ButtonTemplates;
@ -26,25 +37,26 @@ function ArrayFieldItemTemplate(props: ArrayFieldTemplateItemType) {
paddingLeft: 6, paddingLeft: 6,
paddingRight: 6, paddingRight: 6,
fontWeight: 'bold', fontWeight: 'bold',
minWidth: 0,
}; };
return ( return (
<Grid container alignItems="center"> <div className={className}>
<Grid item xs style={{ overflow: 'auto' }}> <div className={hasToolbar ? 'col-xs-9' : 'col-xs-12'}>{children}</div>
<Box mb={2}>
<Paper elevation={2}>
<Box p={2}>{children}</Box>
</Paper>
</Box>
</Grid>
{hasToolbar && ( {hasToolbar && (
<Grid item> <div className="col-xs-3 array-item-toolbox">
<div
className="btn-group"
style={{
display: 'flex',
justifyContent: 'space-around',
}}
>
{(hasMoveUp || hasMoveDown) && ( {(hasMoveUp || hasMoveDown) && (
<MoveUpButton <MoveUpButton
style={btnStyle} style={btnStyle}
disabled={disabled || readonly || !hasMoveUp} disabled={disabled || readonly || !hasMoveUp}
onClick={onReorderClick(index, index - 1)} onClick={onReorderClick(index, index - 1)}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry}
/> />
)} )}
{(hasMoveUp || hasMoveDown) && ( {(hasMoveUp || hasMoveDown) && (
@ -53,6 +65,7 @@ function ArrayFieldItemTemplate(props: ArrayFieldTemplateItemType) {
disabled={disabled || readonly || !hasMoveDown} disabled={disabled || readonly || !hasMoveDown}
onClick={onReorderClick(index, index + 1)} onClick={onReorderClick(index, index + 1)}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry}
/> />
)} )}
{hasRemove && ( {hasRemove && (
@ -61,12 +74,12 @@ function ArrayFieldItemTemplate(props: ArrayFieldTemplateItemType) {
disabled={disabled || readonly} disabled={disabled || readonly}
onClick={onDropIndexClick(index)} onClick={onDropIndexClick(index)}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry}
/> />
)} )}
</Grid> </div>
</div>
)} )}
</Grid> </div>
); );
} }
export default ArrayFieldItemTemplate;

View File

@ -1,17 +1,26 @@
import React from 'react'; import React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import { import {
ArrayFieldTemplateItemType,
ArrayFieldTemplateProps,
getTemplate, getTemplate,
getUiOptions, getUiOptions,
ArrayFieldTemplateProps,
ArrayFieldTemplateItemType,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
} from '@rjsf/utils'; } from '@rjsf/utils';
function ArrayFieldTemplate(props: ArrayFieldTemplateProps) { /** 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 { const {
canAdd, canAdd,
className,
disabled, disabled,
idSchema, idSchema,
uiSchema, uiSchema,
@ -23,36 +32,36 @@ function ArrayFieldTemplate(props: ArrayFieldTemplateProps) {
schema, schema,
title, title,
} = props; } = props;
const uiOptions = getUiOptions(uiSchema); const uiOptions = getUiOptions<T, S, F>(uiSchema);
const ArrayFieldDescriptionTemplate = const ArrayFieldDescriptionTemplate = getTemplate<
getTemplate<'ArrayFieldDescriptionTemplate'>(
'ArrayFieldDescriptionTemplate', 'ArrayFieldDescriptionTemplate',
registry, T,
uiOptions S,
); F
const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate'>( >('ArrayFieldDescriptionTemplate', registry, uiOptions);
const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate', T, S, F>(
'ArrayFieldItemTemplate', 'ArrayFieldItemTemplate',
registry, registry,
uiOptions uiOptions
); );
const ArrayFieldTitleTemplate = getTemplate<'ArrayFieldTitleTemplate'>( const ArrayFieldTitleTemplate = getTemplate<
'ArrayFieldTitleTemplate', 'ArrayFieldTitleTemplate',
registry, T,
uiOptions S,
); F
>('ArrayFieldTitleTemplate', registry, uiOptions);
// Button templates are not overridden in the uiSchema // Button templates are not overridden in the uiSchema
const { const {
ButtonTemplates: { AddButton }, ButtonTemplates: { AddButton },
} = registry.templates; } = registry.templates;
return ( return (
<Paper elevation={2}> <fieldset className={className} id={idSchema.$id}>
<Box p={2}>
<ArrayFieldTitleTemplate <ArrayFieldTitleTemplate
idSchema={idSchema} idSchema={idSchema}
title={uiOptions.title || title} title={uiOptions.title || title}
required={required}
schema={schema} schema={schema}
uiSchema={uiSchema} uiSchema={uiSchema}
required={required}
registry={registry} registry={registry}
/> />
<ArrayFieldDescriptionTemplate <ArrayFieldDescriptionTemplate
@ -62,29 +71,23 @@ function ArrayFieldTemplate(props: ArrayFieldTemplateProps) {
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry} registry={registry}
/> />
<Grid container key={`array-item-list-${idSchema.$id}`}> <div className="row array-item-list">
{items && {items &&
items.map(({ key, ...itemProps }: ArrayFieldTemplateItemType) => ( items.map(
({ key, ...itemProps }: ArrayFieldTemplateItemType<T, S, F>) => (
<ArrayFieldItemTemplate key={key} {...itemProps} /> <ArrayFieldItemTemplate key={key} {...itemProps} />
))} )
)}
</div>
{canAdd && ( {canAdd && (
<Grid container justifyContent="flex-end">
<Grid item>
<Box mt={2}>
<AddButton <AddButton
className="array-item-add" className="array-item-add"
onClick={onAddClick} onClick={onAddClick}
disabled={disabled || readonly} disabled={disabled || readonly}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry}
/> />
</Box>
</Grid>
</Grid>
)} )}
</Grid> </fieldset>
</Box>
</Paper>
); );
} }
export default ArrayFieldTemplate;

View File

@ -0,0 +1,29 @@
import React from "react";
const REQUIRED_FIELD_SYMBOL = "*";
export type LabelProps = {
/** The label for the field */
label?: string;
/** A boolean value stating if the field is required */
required?: boolean;
/** The id of the input field being labeled */
id?: string;
};
/** Renders a label for a field
*
* @param props - The `LabelProps` for this component
*/
export default function Label(props: LabelProps) {
const { label, required, id } = props;
if (!label) {
return null;
}
return (
<label className="control-label" htmlFor={id}>
{label}
{required && <span className="required">{REQUIRED_FIELD_SYMBOL}</span>}
</label>
);
}

View File

@ -1,89 +1,87 @@
import React from 'react'; import React from 'react';
import Grid from '@mui/material/Grid';
import { import {
FormContextType,
ObjectFieldTemplatePropertyType,
ObjectFieldTemplateProps, ObjectFieldTemplateProps,
RJSFSchema,
StrictRJSFSchema,
canExpand, canExpand,
getTemplate, getTemplate,
getUiOptions, getUiOptions,
} from '@rjsf/utils'; } from '@rjsf/utils';
function ObjectFieldTemplate({ /** The `ObjectFieldTemplate` is the template to use to render all the inner properties of an object along with the
* title and description if available. If the object is expandable, then an `AddButton` is also rendered after all
* the properties.
*
* @param props - The `ObjectFieldTemplateProps` for this component
*/
export default function ObjectFieldTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: ObjectFieldTemplateProps<T, S, F>) {
const {
description, description,
title,
properties,
required,
disabled, disabled,
readonly,
uiSchema,
idSchema,
schema,
formData, formData,
idSchema,
onAddClick, onAddClick,
properties,
readonly,
registry, registry,
}: ObjectFieldTemplateProps) { required,
const uiOptions = getUiOptions(uiSchema); schema,
const TitleFieldTemplate = getTemplate<'TitleFieldTemplate'>( title,
uiSchema,
} = props;
const options = getUiOptions<T, S, F>(uiSchema);
const TitleFieldTemplate = getTemplate<'TitleFieldTemplate', T, S, F>(
'TitleFieldTemplate', 'TitleFieldTemplate',
registry, registry,
uiOptions options
); );
const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate'>( const DescriptionFieldTemplate = getTemplate<
'DescriptionFieldTemplate', 'DescriptionFieldTemplate',
registry, T,
uiOptions S,
); F
>('DescriptionFieldTemplate', registry, options);
// Button templates are not overridden in the uiSchema // Button templates are not overridden in the uiSchema
const { const {
ButtonTemplates: { AddButton }, ButtonTemplates: { AddButton },
} = registry.templates; } = registry.templates;
return ( return (
<> <fieldset id={idSchema.$id}>
{(uiOptions.title || title) && ( {(options.title || title) && (
<TitleFieldTemplate <TitleFieldTemplate
id={`${idSchema.$id}-title`} id={`${idSchema.$id}__title`}
title={title} title={options.title || title}
required={required} required={required}
schema={schema} schema={schema}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry} registry={registry}
/> />
)} )}
{(uiOptions.description || description) && ( {(options.description || description) && (
<DescriptionFieldTemplate <DescriptionFieldTemplate
id={`${idSchema.$id}-description`} id={`${idSchema.$id}__description`}
description={uiOptions.description || description!} description={options.description || description!}
schema={schema} schema={schema}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry} registry={registry}
/> />
)} )}
<Grid container spacing={2} style={{ marginTop: '10px' }}> {properties.map((prop: ObjectFieldTemplatePropertyType) => prop.content)}
{properties.map((element, index) => {canExpand<T, S, F>(schema, uiSchema, formData) && (
// Remove the <Grid> if the inner element is hidden as the <Grid>
// itself would otherwise still take up space.
element.hidden ? (
element.content
) : (
<Grid item xs={12} key={index} style={{ marginBottom: '10px' }}>
{element.content}
</Grid>
)
)}
{canExpand(schema, uiSchema, formData) && (
<Grid container justifyContent="flex-end">
<Grid item>
<AddButton <AddButton
className="object-property-expand" className="object-property-expand"
onClick={onAddClick(schema)} onClick={onAddClick(schema)}
disabled={disabled || readonly} disabled={disabled || readonly}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry}
/> />
</Grid>
</Grid>
)} )}
</Grid> </fieldset>
</>
); );
} }
export default ObjectFieldTemplate;

View File

@ -1,80 +1,73 @@
import React, { CSSProperties } from 'react';
import FormControl from '@mui/material/FormControl';
import Grid from '@mui/material/Grid';
import InputLabel from '@mui/material/InputLabel';
import Input from '@mui/material/OutlinedInput';
import { import {
ADDITIONAL_PROPERTY_FLAG, ADDITIONAL_PROPERTY_FLAG,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WrapIfAdditionalTemplateProps, WrapIfAdditionalTemplateProps,
} from '@rjsf/utils'; } from '@rjsf/utils';
function WrapIfAdditionalTemplate({ import Label from '../FieldTemplate/Label';
children,
/** The `WrapIfAdditional` component is used by the `FieldTemplate` to rename, or remove properties that are
* part of an `additionalProperties` part of a schema.
*
* @param props - The `WrapIfAdditionalProps` for this component
*/
export default function WrapIfAdditionalTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>(props: WrapIfAdditionalTemplateProps<T, S, F>) {
const {
id,
classNames, classNames,
disabled, disabled,
id,
label, label,
onDropPropertyClick,
onKeyChange, onKeyChange,
onDropPropertyClick,
readonly, readonly,
required, required,
schema, schema,
children,
uiSchema, uiSchema,
registry, registry,
}: WrapIfAdditionalTemplateProps) { } = props;
// Button templates are not overridden in the uiSchema // Button templates are not overridden in the uiSchema
const { RemoveButton } = registry.templates.ButtonTemplates; const { RemoveButton } = registry.templates.ButtonTemplates;
const keyLabel = `${label} Key`; // i18n ? const keyLabel = `${label} Key`; // i18n ?
const additional = ADDITIONAL_PROPERTY_FLAG in schema; const additional = ADDITIONAL_PROPERTY_FLAG in schema;
const btnStyle: CSSProperties = {
flex: 1,
paddingLeft: 6,
paddingRight: 6,
fontWeight: 'bold',
};
if (!additional) { if (!additional) {
return <div className={classNames}>{children}</div>; return <div className={classNames}>{children}</div>;
} }
const handleBlur = ({ target }: React.FocusEvent<HTMLInputElement>) =>
onKeyChange(target.value);
return ( return (
<Grid <div className={classNames}>
container <div className="row">
key={`${id}-key`} <div className="col-xs-5 form-additional">
alignItems="center" <div className="form-group">
spacing={2} <Label label={keyLabel} required={required} id={`${id}-key`} />
className={classNames} <input
> className="form-control"
<Grid item xs>
<FormControl fullWidth required={required}>
<InputLabel>{keyLabel}</InputLabel>
<Input
defaultValue={label}
disabled={disabled || readonly}
id={`${id}-key`}
name={`${id}-key`}
onBlur={!readonly ? handleBlur : undefined}
type="text" type="text"
id={`${id}-key`}
onBlur={(event) => onKeyChange(event.target.value)}
defaultValue={label}
/> />
</FormControl> </div>
</Grid> </div>
<Grid item xs> <div className="form-additional form-group col-xs-5">{children}</div>
{children} <div className="col-xs-2">
</Grid>
<Grid item>
<RemoveButton <RemoveButton
iconType="default" className="array-item-remove btn-block"
style={btnStyle} style={{ border: '0' }}
disabled={disabled || readonly} disabled={disabled || readonly}
onClick={onDropPropertyClick(label)} onClick={onDropPropertyClick(label)}
uiSchema={uiSchema} uiSchema={uiSchema}
registry={registry}
/> />
</Grid> </div>
</Grid> </div>
</div>
); );
} }
export default WrapIfAdditionalTemplate;