Skip to content

Commit 4616bb6

Browse files
Feature - Optional Data Controls (rjsf-team#4798)
* Feature - Optional Data Controls Added new `Optional Data Controls` feature as follows: - In `@rjsf/utils`: - Updated existing tests where `getDefaultFormState` is used to reflect addition of `initialDefaultsGenerated` - Updated `types.ts` to support the new `Optional Data Controls` feature as follows: - Added new `OptionalDataControlsTemplateProps` and refactored the common props from `ArrayFieldTemplateProps` and `ObjectFieldTemplateProps` into a new super type, `ContainerFieldTemplateProps` - Added new `optionalDataControl?: ReactNode` to the `ArrayFieldTitleProps`, `TitleFieldProps` and `ContainerFieldTemplateProps` - Updated `GlobalFormOptions` to add new `enableOptionalDataFieldForType?: ('object' | 'array')[]` prop - Updated `SchemaUtilsType`'s `retrieveSchema()` function to add an additional, property `resolveAnyOfOrOneOfRefs?: boolean` - Updated the `Templates` interface to add a new required template `OptionalDataControlsTemplate: ComponentType<OptionalDataControlsTemplateProps<T, S, F>>` - Updated `retrieveSchema()` to add an additional property `resolveAnyOfOrOneOfRefs?: boolean` which causes `resolveAllSchemas()` to resolve `$ref`s inside of the options of `anyOf`/`oneOf` schemas - Updated `getDefaultFormState` to fix an issue where optional array props had their default set to an empty array when they shouldn't be - Updated the `TranslatableString` enum to add three new strings in support of the new feature: `OptionalObjectAdd`, `OptionalObjectRemove` and `OptionalObjectEmptyMsg` - Added three new utility functions: `isFormDataAvailable()`, `isRootSchema()` and `shouldRenderOptionalField()` - Added or updated tests to verify all of the new behaviors - In `@rjsf/core`: - Added a new `OptionalDataControlsField` to the `fields` that renders either undefined (when there is data for a readonly/disabled field) or gets the `OptionalDataControlsTemplate` and renders the `label` and potentially an `onAddClick` or `onRemoveClick` function - Updated `ArrayField` and `ObjectField` to check whether it `shouldRenderOptionalData()` and if true, calls `ObjectDataControlsField` and passes the result to its associated render template as `optionalDataControl` - Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature - Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list - In the rest of the themes: - Updated `ArrayFieldTemplate`, `ArrayFieldTitleTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature - Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list - Updated the `ButtonTemplates` classes to better support the `OptionalDataControlTemplate` - In the doc directory: - Updated `utility-function.me` docs to add documentation for the new functions - Also updated docs for `retrieveSchema` and `SchemaUtilsType` for the new prop - Updated `uiSchema.md` to add documentation for the new `enableOptionalDataFieldForType` prop - Updated the `v6x upgrade guide.md` to document the new feature and utility functions and changes to `retrieveSchema` - Updated the playground to add a new `Optional Data Controls` example - Updated the snapshot and jest tests for `Form` to test the new `Optional Data Controls` feature - Updated the `CHANGELOG.md` file accordingly * - Added the snapsnot test for `FormTest.tsx` and ran it on all the themes * - Added new `optionalControlsId()` function, using it to add ids to all of the data controls * - Finished up all of the testing * - Added/updated documentation * Apply suggestions from code review -Responded to reviewer feedback Co-authored-by: Nick Grosenbacher <[email protected]> --------- Co-authored-by: Nick Grosenbacher <[email protected]>
1 parent f4cd0a5 commit 4616bb6

File tree

145 files changed

+82563
-450
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+82563
-450
lines changed

CHANGELOG.md

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,98 @@ should change the heading of the (upcoming) version to include a major version b
1717
-->
1818
# 6.0.0-beta.21
1919

20+
## @rjsf/antd
21+
22+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
23+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
24+
- Updated the `ButtonTemplates` classes to fix up the props in `AntdIconButtonProps` and the `IconButton`s associated with them to better support the `OptionalDataControlTemplate`
25+
26+
## @rjsf/chakra-ui
27+
28+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
29+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
30+
- Updated the `ButtonTemplates` classes to add `ChakraIconButtonProps` and the `IconButton`s associated with them to better support the `OptionalDataControlTemplate`
31+
2032
## @rjsf/core
2133

2234
- Added `initialDefaultsGenerated` flag to state, which indicates whether the initial generation of defaults has been completed
23-
- Added `ObjectField` tests for additionalProperties with defaults
35+
- Added `ObjectField` tests for additionalProperties with defaults
36+
- Added a new `OptionalDataControlsField` to the `fields` that renders either undefined (when there is data for a readonly/disabled field) or gets the `OptionalDataControlsTemplate` and renders the `label` and potentially an `onAddClick` or `onRemoveClick` function
37+
- Updated `ArrayField` and `ObjectField` to check whether it `shouldRenderOptionalData()` and if true, calls `ObjectDataControlsField` and passes the result to its associated render template as `optionalDataControl`
38+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
39+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
40+
41+
## @rjsf/daisyui
42+
43+
- Updated `ArrayFieldTemplate`, `ArrayFieldTitleTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
44+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
45+
- Updated the `ButtonTemplates` classes to better support the `OptionalDataControlTemplate`
46+
47+
## @rjsf/fluentui-rc
48+
49+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
50+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
51+
- Updated the `ButtonTemplates` classes to add `FluentIconButtonProps` and the `IconButton`s associated with them to better support the `OptionalDataControlTemplate`
52+
53+
## @rjsf/mantine
54+
55+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
56+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
57+
58+
## @rjsf/mui
59+
60+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
61+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
62+
63+
## @rjsf/primereact
64+
65+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
66+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
67+
- Updated the `ButtonTemplates` classes to add `PrimeIconButtonProps` and the `IconButton`s associated with them to better support the `OptionalDataControlTemplate`
68+
69+
## @rjsf/react-bootstrap
70+
71+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
72+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
73+
- Updated the `ButtonTemplates` classes to add `BootstrapIconButtonProps` and the `IconButton`s associated with them to better support the `OptionalDataControlTemplate`
74+
75+
## @rjsf/semantic-ui
76+
77+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
78+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
79+
- Updated the `ButtonTemplates` classes to add `SemanticIconButtonProps` and the `IconButton`s associated with them to better support the `OptionalDataControlTemplate`
80+
81+
## @rjsf/shadcn
82+
83+
- Updated `ArrayFieldTemplate`, `ObjectFieldTemplate`, `TitleField` to add support for the new `optionalDataControl` feature
84+
- Added the new `OptionalDataControlTemplate` to the theme, adding it to the `templates` list
85+
- Updated the `ButtonTemplates` classes to add `ShadIconButtonProps` and the `IconButton`s associated with them to better support the `OptionalDataControlTemplate`
2486

2587
## @rjsf/utils
2688

2789
- Updated `getDefaultFormState` to add a new `initialDefaultsGenerated` prop flag, along with type definitions, fixing uneditable & permanent defaults with additional properties [3759](https://github.com/rjsf-team/react-jsonschema-form/issues/3759)
2890
- Updated `createSchemaUtils` definition to reflect addition of `initialDefaultsGenerated`
2991
- Updated existing tests where `getDefaultFormState` is used to reflect addition of `initialDefaultsGenerated`
30-
31-
## @rjsf/docs
32-
33-
- Updated docs for `getDefaultFormState` to reflect addition of `initialDefaultsGenerated` prop
92+
- Updated `types.ts` to support the new `Optional Data Controls` feature as follows:
93+
- Added new `OptionalDataControlsTemplateProps` and refactored the common props from `ArrayFieldTemplateProps` and `ObjectFieldTemplateProps` into a new super type, `ContainerFieldTemplateProps`
94+
- Added new `optionalDataControl?: ReactNode` to the `ArrayFieldTitleProps`, `TitleFieldProps` and `ContainerFieldTemplateProps`
95+
- Updated `GlobalFormOptions` to add new `enableOptionalDataFieldForType?: ('object' | 'array')[]` prop
96+
- Updated `SchemaUtilsType`'s `retrieveSchema()` function to add an additional, property `resolveAnyOfOrOneOfRefs?: boolean`
97+
- Updated the `Templates` interface to add a new required template `OptionalDataControlsTemplate: ComponentType<OptionalDataControlsTemplateProps<T, S, F>>`
98+
- Updated `retrieveSchema()` to add an additional property `resolveAnyOfOrOneOfRefs?: boolean` which causes `resolveAllSchemas()` to resolve `$ref`s inside of the options of `anyOf`/`oneOf` schemas
99+
- Updated `getDefaultFormState` to fix an issue where optional array props had their default set to an empty array when they shouldn't be
100+
- Updated the `TranslatableString` enum to add three new strings in support of the new feature: `OptionalObjectAdd`, `OptionalObjectRemove` and `OptionalObjectEmptyMsg`
101+
- Added four new utility functions: `isFormDataAvailable()`, `isRootSchema()`, `optionalControlsId()`, and `shouldRenderOptionalField()`
102+
103+
## Dev / docs / playground
104+
105+
- Updated docs for `getDefaultFormState` to reflect addition of the `initialDefaultsGenerated` prop
106+
- Updated `utility-function.me` docs to add documentation for the new functions
107+
- Also updated docs for `retrieveSchema` and `SchemaUtilsType` for the new prop
108+
- Updated `uiSchema.md` to add documentation for the new `enableOptionalDataFieldForType` prop
109+
- Updated the `v6x upgrade guide.md` to document the new feature and utility functions and changes to `retrieveSchema`
110+
- Updated the playground to add a new `Optional Data Controls` example
111+
- Updated the snapshot and jest tests for `Form` to test the new `Optional Data Controls` feature
34112

35113
# 6.0.0-beta-20
36114

packages/antd/src/templates/ArrayFieldTemplate/index.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default function ArrayFieldTemplate<
3232
disabled,
3333
fieldPathId,
3434
items,
35+
optionalDataControl,
3536
onAddClick,
3637
readonly,
3738
registry,
@@ -56,6 +57,7 @@ export default function ArrayFieldTemplate<
5657
registry,
5758
uiOptions,
5859
);
60+
const showOptionalDataControlInTitle = !readonly && !disabled;
5961
const { formContext } = registry;
6062
// Button templates are not overridden in the uiSchema
6163
const {
@@ -84,6 +86,7 @@ export default function ArrayFieldTemplate<
8486
schema={schema}
8587
uiSchema={uiSchema}
8688
registry={registry}
89+
optionalDataControl={showOptionalDataControlInTitle ? optionalDataControl : undefined}
8790
/>
8891
</Col>
8992
)}
@@ -99,12 +102,11 @@ export default function ArrayFieldTemplate<
99102
</Col>
100103
)}
101104
<Col className='row array-item-list' span={24}>
102-
{items &&
103-
items.map(({ key, ...itemProps }: ArrayFieldItemTemplateType<T, S, F>) => (
104-
<ArrayFieldItemTemplate key={key} {...itemProps} />
105-
))}
105+
{!showOptionalDataControlInTitle ? optionalDataControl : undefined}
106+
{items.map(({ key, ...itemProps }: ArrayFieldItemTemplateType<T, S, F>) => (
107+
<ArrayFieldItemTemplate key={key} {...itemProps} />
108+
))}
106109
</Col>
107-
108110
{canAdd && (
109111
<Col span={24}>
110112
<Row gutter={rowGutter} justify='end'>

packages/antd/src/templates/IconButton/index.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,24 @@ import {
1414
} from '@rjsf/utils';
1515
import { MouseEventHandler } from 'react';
1616

17-
// The `type` and `color` for IconButtonProps collides with props of `ButtonProps` so omit it to avoid Typescript issue
1817
export type AntdIconButtonProps<
1918
T = any,
2019
S extends StrictRJSFSchema = RJSFSchema,
2120
F extends FormContextType = any,
22-
> = Omit<IconButtonProps<T, S, F>, 'type' | 'color'>;
21+
> = IconButtonProps<T, S, F> & Pick<ButtonProps, 'block' | 'danger' | 'size'>;
2322

2423
export default function IconButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
25-
props: AntdIconButtonProps<T, S, F> & Omit<ButtonProps, 'onClick'>,
24+
props: AntdIconButtonProps<T, S, F>,
2625
) {
27-
const { iconType = 'default', icon, onClick, uiSchema, registry, ...otherProps } = props;
26+
const { iconType = 'default', icon, onClick, uiSchema, registry, color, ...otherProps } = props;
2827
return (
2928
<Button
3029
onClick={onClick as MouseEventHandler<HTMLAnchorElement> & MouseEventHandler<HTMLButtonElement>}
31-
type={iconType as ButtonProps['type']}
30+
// @ts-expect-error TS2322, Because even casting as `ButtonProps['type']` has issues
31+
type={iconType}
3232
icon={icon}
33+
color={color as ButtonProps['color']}
34+
style={{ paddingTop: '4px' /* Center the button */ }}
3335
{...otherProps}
3436
/>
3537
);
@@ -44,9 +46,9 @@ export function AddButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
4446
return (
4547
<IconButton
4648
title={translateString(TranslatableString.AddItemButton)}
47-
{...props}
48-
block
4949
iconType='primary'
50+
block
51+
{...props}
5052
icon={<PlusCircleOutlined />}
5153
/>
5254
);
@@ -92,10 +94,10 @@ export function RemoveButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F
9294
return (
9395
<IconButton
9496
title={translateString(TranslatableString.RemoveButton)}
95-
{...props}
9697
danger
9798
block={!!options.block}
9899
iconType='primary'
100+
{...props}
99101
icon={<DeleteOutlined />}
100102
/>
101103
);

packages/antd/src/templates/ObjectFieldTemplate/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export default function ObjectFieldTemplate<
4141
formData,
4242
fieldPathId,
4343
onAddClick,
44+
optionalDataControl,
4445
properties,
4546
readonly,
4647
required,
@@ -57,6 +58,7 @@ export default function ObjectFieldTemplate<
5758
uiOptions,
5859
);
5960
const { formContext } = registry;
61+
const showOptionalDataControlInTitle = !readonly && !disabled;
6062
// Button templates are not overridden in the uiSchema
6163
const {
6264
ButtonTemplates: { AddButton },
@@ -126,6 +128,7 @@ export default function ObjectFieldTemplate<
126128
schema={schema}
127129
uiSchema={uiSchema}
128130
registry={registry}
131+
optionalDataControl={showOptionalDataControlInTitle ? optionalDataControl : undefined}
129132
/>
130133
</Col>
131134
)}
@@ -140,6 +143,7 @@ export default function ObjectFieldTemplate<
140143
/>
141144
</Col>
142145
)}
146+
{!showOptionalDataControlInTitle ? <Col span={24}>{optionalDataControl}</Col> : undefined}
143147
{properties
144148
.filter((e) => !e.hidden)
145149
.map((element: ObjectFieldTemplatePropertyType) => (
@@ -148,7 +152,6 @@ export default function ObjectFieldTemplate<
148152
</Col>
149153
))}
150154
</Row>
151-
152155
{canExpand(schema, uiSchema, formData) && (
153156
<Col span={24}>
154157
<Row gutter={rowGutter} justify='end'>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { FormContextType, OptionalDataControlsTemplateProps, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
2+
3+
import { AddButton, RemoveButton } from '../IconButton';
4+
5+
/** The OptionalDataControlsTemplate renders one of three different states. If
6+
* there is an `onAddClick()` function, it renders the "Add" button. If there is
7+
* an `onRemoveClick()` function, it renders the "Remove" button. Otherwise it
8+
* renders the "No data found" section. All of them use the `label` as either
9+
* the `title` of buttons or simply outputting it.
10+
*
11+
* @param props - The `OptionalDataControlsTemplateProps` for the template
12+
*/
13+
export default function OptionalDataControlsTemplate<
14+
T = any,
15+
S extends StrictRJSFSchema = RJSFSchema,
16+
F extends FormContextType = any,
17+
>(props: OptionalDataControlsTemplateProps<T, S, F>) {
18+
const { id, registry, label, onAddClick, onRemoveClick } = props;
19+
if (onAddClick) {
20+
return (
21+
<AddButton
22+
id={id}
23+
registry={registry}
24+
className='rjsf-add-optional-data'
25+
onClick={onAddClick}
26+
title={label}
27+
size='small'
28+
iconType='default'
29+
block={false}
30+
/>
31+
);
32+
} else if (onRemoveClick) {
33+
return (
34+
<RemoveButton
35+
id={id}
36+
registry={registry}
37+
className='rjsf-remove-optional-data'
38+
onClick={onRemoveClick}
39+
title={label}
40+
size='small'
41+
iconType='default'
42+
block={false}
43+
/>
44+
);
45+
}
46+
return <em id={id}>{label}</em>;
47+
}

packages/antd/src/templates/TitleField/index.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import classNames from 'classnames';
22
import { FormContextType, TitleFieldProps, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
3-
import { ConfigProvider } from 'antd';
3+
import { Col, Divider, Row, ConfigProvider } from 'antd';
44
import { useContext } from 'react';
55

66
/** The `TitleField` is the template to use to render the title of a field
@@ -12,6 +12,7 @@ export default function TitleField<T = any, S extends StrictRJSFSchema = RJSFSch
1212
required,
1313
registry,
1414
title,
15+
optionalDataControl,
1516
}: TitleFieldProps<T, S, F>) {
1617
const { formContext } = registry;
1718
const { colon = true } = formContext;
@@ -38,8 +39,7 @@ export default function TitleField<T = any, S extends StrictRJSFSchema = RJSFSch
3839
[`${prefixCls}-item-required`]: required,
3940
[`${prefixCls}-item-no-colon`]: !colon,
4041
});
41-
42-
return title ? (
42+
let heading = title ? (
4343
<label
4444
className={labelClassName}
4545
htmlFor={id}
@@ -49,4 +49,19 @@ export default function TitleField<T = any, S extends StrictRJSFSchema = RJSFSch
4949
{labelChildren}
5050
</label>
5151
) : null;
52+
if (optionalDataControl) {
53+
heading = (
54+
<Row>
55+
<Col flex='auto'>{heading}</Col>
56+
<Col flex='none'>{optionalDataControl}</Col>
57+
</Row>
58+
);
59+
}
60+
61+
return (
62+
<>
63+
{heading}
64+
<Divider size='small' style={{ marginBlock: '1px' /* pull the margin right up against the label */ }} />
65+
</>
66+
);
5267
}

packages/antd/src/templates/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import FieldTemplate from './FieldTemplate';
1111
import GridTemplate from './GridTemplate';
1212
import MultiSchemaFieldTemplate from './MultiSchemaFieldTemplate';
1313
import ObjectFieldTemplate from './ObjectFieldTemplate';
14+
import OptionalDataControlsTemplate from './OptionalDataControlsTemplate';
1415
import SubmitButton from './SubmitButton';
1516
import TitleField from './TitleField';
1617
import WrapIfAdditionalTemplate from './WrapIfAdditionalTemplate';
@@ -39,6 +40,7 @@ export function generateTemplates<
3940
GridTemplate,
4041
MultiSchemaFieldTemplate,
4142
ObjectFieldTemplate,
43+
OptionalDataControlsTemplate,
4244
TitleFieldTemplate: TitleField,
4345
WrapIfAdditionalTemplate,
4446
};

0 commit comments

Comments
 (0)