Skip to content

Commit

Permalink
Skip populate function proposal in Experimental_ArrayMinItems (rjsf-t…
Browse files Browse the repository at this point in the history
…eam#4121)

* skip populate draft

* Added a new `computeSkipPopulate` option in `arrayMinItems`, allowing custom logic to skip populating arrays with default values

* Add example in documentation of computeSkipPopulate

* Update packages/docs/docs/api-reference/form-props.md

Co-authored-by: Heath C <51679588+heath-freenome@users.noreply.github.com>

* Update packages/utils/src/types.ts

Co-authored-by: Heath C <51679588+heath-freenome@users.noreply.github.com>

* Update packages/docs/docs/api-reference/form-props.md

Co-authored-by: Heath C <51679588+heath-freenome@users.noreply.github.com>

---------

Co-authored-by: Marek Bodinger <marek.bodinger@gmail.com>
Co-authored-by: Heath C <51679588+heath-freenome@users.noreply.github.com>
  • Loading branch information
3 people committed Mar 21, 2024
1 parent bb2093d commit baffbf1
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ should change the heading of the (upcoming) version to include a major version b
## @rjsf/utils

- Added a new `skipEmptyDefault` option in `emptyObjectFields`, fixing [#3880](https://github.com/rjsf-team/react-jsonschema-form/issues/3880)
- Added a new `computeSkipPopulate` option in `arrayMinItems`, allowing custom logic to skip populating arrays with default values, implementing [#4121](https://github.com/rjsf-team/react-jsonschema-form/pull/4121).
- Fixed bug where the string `"\</strong>"` would get printed next to filenames when uploading files, and restored intended bolding of filenames fixing [#4120](https://github.com/rjsf-team/react-jsonschema-form/issues/4120).

## Dev / docs / playground
Expand Down
64 changes: 64 additions & 0 deletions packages/docs/docs/api-reference/form-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,70 @@ Optional enumerated flag controlling how array minItems are populated, defaultin
| `requiredOnly` | Ignore `minItems` on a field when calculating defaults unless the field is required. |
| `never` | Ignore `minItems` on a field when calculating defaults for required and non-required. Value will set only if defined `default` and from `formData` |

#### `arrayMinItems.computeSkipPopulate`

The signature and documentation for this property is as follow:

##### computeSkipPopulate <T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()

A function that determines whether to skip populating the array with default values based on the provided validator, schema, and root schema.
If the function returns `true`, the array will not be populated with default values.
If the function returns `false`, the array will be populated with default values according to the `populate` option.

###### Parameters

- validator: ValidatorType<T, S, F> - An implementation of the `ValidatorType` interface that is used to detect valid schema conditions
- schema: S - The schema for which resolving a condition is desired
- [rootSchema]: S - The root schema that will be forwarded to all the APIs

###### Returns

- boolean: A boolean indicating whether to skip populating the array with default values.


##### Example

```tsx
import { RJSFSchema } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';

const schema: RJSFSchema = {
type: 'object',
properties: {
stringArray: {
type: 'array',
items: { type: 'string' },
minItems: 1,
},
numberArray: {
type: 'array',
items: { type: 'number' },
minItems: 1,
},
},
required: ['stringArray', 'numberArray'],
};

const computeSkipPopulateNumberArrays = (validator, schema, rootSchema) =>
// These conditions are needed to narrow down the type of the schema.items
!Array.isArray(schema?.items) &&
typeof schema?.items !== 'boolean' &&
schema?.items?.type === 'number',

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

#### `arrayMinItems.mergeExtraDefaults`

Optional boolean flag, defaulting to `false` when not specified.
Expand Down
3 changes: 3 additions & 0 deletions packages/utils/src/schema/getDefaultFormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
const neverPopulate = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'never';
const ignoreMinItemsFlagSet = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'requiredOnly';
const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults';
const computeSkipPopulate =
experimental_defaultFormStateBehavior?.arrayMinItems?.computeSkipPopulate ?? (() => false);

const emptyDefault = isSkipEmptyDefaults ? undefined : [];

Expand Down Expand Up @@ -399,6 +401,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
if (
!schema.minItems ||
isMultiSelect<T, S, F>(validator, schema, rootSchema) ||
computeSkipPopulate<T, S, F>(validator, schema, rootSchema) ||
schema.minItems <= defaultsLength
) {
return defaults ? defaults : emptyDefault;
Expand Down
14 changes: 14 additions & 0 deletions packages/utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ export type Experimental_ArrayMinItems = {
* - `never`: Ignore `minItems` on a field even the field is required.
*/
populate?: 'all' | 'requiredOnly' | 'never';
/** A function that determines whether to skip populating the array with default values based on the provided validator,
* schema, and root schema.
* If the function returns true, the array will not be populated with default values.
* If the function returns false, the array will be populated with default values according to the `populate` option.
* @param validator - An implementation of the `ValidatorType` interface that is used to detect valid schema conditions
* @param schema - The schema for which resolving a condition is desired
* @param [rootSchema] - The root schema that will be forwarded to all the APIs
* @returns A boolean indicating whether to skip populating the array with default values.
*/
computeSkipPopulate?: <T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
validator: ValidatorType<T, S, F>,
schema: S,
rootSchema?: S
) => boolean;
/** When `formData` is provided and does not contain `minItems` worth of data, this flag (`false` by default) controls
* whether the extra data provided by the defaults is appended onto the existing `formData` items to ensure the
* `minItems` condition is met. When false (legacy behavior), only the `formData` provided is merged into the default
Expand Down
26 changes: 26 additions & 0 deletions packages/utils/test/schema/getDefaultFormStateTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2872,5 +2872,31 @@ export default function getDefaultFormStateTest(testValidator: TestValidatorType
})
).toEqual({ requiredArray: ['raw0', 'default0'] });
});
it('should not populate defaults for array items when computeSkipPopulate returns true', () => {
const schema: RJSFSchema = {
type: 'object',
properties: {
stringArray: {
type: 'array',
items: { type: 'string' },
minItems: 1,
},
numberArray: {
type: 'array',
items: { type: 'number' },
minItems: 1,
},
},
required: ['stringArray', 'numberArray'],
};
expect(
getDefaultFormState(testValidator, schema, {}, schema, false, {
arrayMinItems: {
computeSkipPopulate: (_, schema) =>
!Array.isArray(schema?.items) && typeof schema?.items !== 'boolean' && schema?.items?.type === 'number',
},
})
).toEqual({ stringArray: [undefined], numberArray: [] });
});
});
}

0 comments on commit baffbf1

Please sign in to comment.