Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG_v6.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ should change the heading of the (upcoming) version to include a major version b
- BREAKING CHANGE: Moved the addition of `Bootstrap 3` classes from the `SchemaField` to the `WrapIfAdditionalTemplate`, thereby affecting all the other themes, fixing [#2280](https://github.com/rjsf-team/react-jsonschema-form/issues/2280)
- BREAKING CHANGE: Added `rjsf-` prefix onto the following marker classes used in the fields and templates:
- `field`, `field-<schema.type>`, `field-error`, `field-hidden`, `field-array`, `field-array-of-<schema.type>`, `field-array-fixed-items`, `array-item`, `config-error`, `array-item-add`, `array-item-copy`, `array-item-move-down`, `array-item-move-up`, `array-item-remove`, `object-property-expand`
- Added support for `patternProperties` [#1944](https://github.com/rjsf-team/react-jsonschema-form/issues/1944)

## @rjsf/daisyui

Expand Down Expand Up @@ -111,6 +112,7 @@ should change the heading of the (upcoming) version to include a major version b
- BREAKING CHANGE: Removed the deprecated `toErrorList()` function from the `ValidatorType` interface
- BREAKING CHANGE: Removed the deprecated `RJSF_ADDITONAL_PROPERTIES_FLAG` constant
- Updated the `WrapIfAdditionalTemplateProps` to include `hideError` and `rawErrors` in support of moving `Bootstrap 3` marker classes out of `SchemaField`
- Added support for `patternProperties` [#1944](https://github.com/rjsf-team/react-jsonschema-form/issues/1944)

## @rjsf/validator-ajv6

Expand All @@ -130,6 +132,7 @@ should change the heading of the (upcoming) version to include a major version b
- Updated the `playground` to add a `Layout Grid` example and made the selected example now be part of the shared export
- Replaced Lerna with Nx, updated all lerna commands to use the Nx CLI
- BREAKING CHANGE: Updated all `peerDependencies` to change minimal `React` support to `>=18`
- Added documentation and playground example for `patternProperties`

# 6.0.0-alpha.0

Expand Down
52 changes: 28 additions & 24 deletions packages/core/src/components/fields/ObjectField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,36 +195,40 @@ class ObjectField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
* @param schema - The schema element to which the new property is being added
*/
handleAddClick = (schema: S) => () => {
if (!schema.additionalProperties) {
if (!(schema.additionalProperties || schema.patternProperties)) {
return;
}
const { formData, onChange, registry } = this.props;
const newFormData = { ...formData } as T;

let type: RJSFSchema['type'] = undefined;
let constValue: RJSFSchema['const'] = undefined;
let defaultValue: RJSFSchema['default'] = undefined;
if (isObject(schema.additionalProperties)) {
type = schema.additionalProperties.type;
constValue = schema.additionalProperties.const;
defaultValue = schema.additionalProperties.default;
let apSchema = schema.additionalProperties;
if (REF_KEY in apSchema) {
const { schemaUtils } = registry;
apSchema = schemaUtils.retrieveSchema({ $ref: apSchema[REF_KEY] } as S, formData);
type = apSchema.type;
constValue = apSchema.const;
defaultValue = apSchema.default;
}
if (!type && (ANY_OF_KEY in apSchema || ONE_OF_KEY in apSchema)) {
type = 'object';
const newKey = this.getAvailableKey('newKey', newFormData);
if (schema.patternProperties) {
// Cast this to make the `set` work properly
set(newFormData as GenericObjectType, newKey, null);
} else {
let type: RJSFSchema['type'] = undefined;
let constValue: RJSFSchema['const'] = undefined;
let defaultValue: RJSFSchema['default'] = undefined;
if (isObject(schema.additionalProperties)) {
type = schema.additionalProperties.type;
constValue = schema.additionalProperties.const;
defaultValue = schema.additionalProperties.default;
let apSchema = schema.additionalProperties;
if (REF_KEY in apSchema) {
const { schemaUtils } = registry;
apSchema = schemaUtils.retrieveSchema({ $ref: apSchema[REF_KEY] } as S, formData);
type = apSchema.type;
constValue = apSchema.const;
defaultValue = apSchema.default;
}
if (!type && (ANY_OF_KEY in apSchema || ONE_OF_KEY in apSchema)) {
type = 'object';
}
}
}

const newKey = this.getAvailableKey('newKey', newFormData);
const newValue = constValue ?? defaultValue ?? this.getDefaultValue(type);
// Cast this to make the `set` work properly
set(newFormData as GenericObjectType, newKey, newValue);
const newValue = constValue ?? defaultValue ?? this.getDefaultValue(type);
// Cast this to make the `set` work properly
set(newFormData as GenericObjectType, newKey, newValue);
}

onChange(newFormData);
};
Expand Down
14 changes: 7 additions & 7 deletions packages/docs/docs/advanced-customization/custom-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ In version 5, all existing `templates` were consolidated into a new `TemplatesTy
They can also be overloaded globally on the `Form` via the `templates` prop as well as globally or per-field through the `uiSchema`.
Further, many new templates were added or repurposed from existing `widgets` and `fields` in an effort to simplify the effort needed by theme authors to build new and/or maintain current themes.
These new templates can also be overridden by individual users to customize the specific needs of their application.
A special category of templates, `ButtonTemplates`, were also added to support the easy replacement of the `Submit` button on the form, the `Add` and `Remove` buttons associated with `additionalProperties` on objects and elements of arrays, as well as the `Move up` and `Move down` buttons used for reordering arrays.
A special category of templates, `ButtonTemplates`, were also added to support the easy replacement of the `Submit` button on the form, the `Add` and `Remove` buttons associated with `additionalProperties` and `patternProperties` on objects and elements of arrays, as well as the `Move up` and `Move down` buttons used for reordering arrays.
This category, unlike the others, can only be overridden globally via the `templates` prop on `Form`.

Below is the table that lists all the `templates`, their props interface, their `uiSchema` name and from where they originated in the previous version of RJSF:
Expand Down Expand Up @@ -461,7 +461,7 @@ The following props are passed to the `BaseInputTemplate`:
- `multiple`: A boolean value stating if the widget can accept multiple values;
- `onChange`: The value change event handler; call it with the new value every time it changes;
- `onChangeOverride`: A `BaseInputTemplate` implements a default `onChange` handler that it passes to the HTML input component to handle the `ChangeEvent`. Sometimes a widget may need to handle the `ChangeEvent` using custom logic. If that is the case, that widget should provide its own handler via this prop;
- `onKeyChange`: The key change event handler (only called for fields with `additionalProperties`); pass the new value every time it changes;
- `onKeyChange`: The key change event handler (only called for fields with `additionalProperties` and `patternProperties`); pass the new value every time it changes;
- `onBlur`: The input blur event handler; call it with the widget id and value;
- `onFocus`: The input focus event handler; call it with the widget id and value;
- `options`: A map of options passed as a prop to the component (see [Custom widget options](./custom-widgets-fields.md#custom-widget-options)).
Expand Down Expand Up @@ -807,7 +807,7 @@ The following props are passed to each `ObjectFieldTemplate` as defined by the `
- `description`: A string value containing the description for the object.
- `disabled`: A boolean value stating if the object is disabled.
- `properties`: An array of object representing the properties in the object. Each of the properties represent a child with properties described below.
- `onAddClick: (schema: RJSFSchema) => () => void`: Returns a function that adds a new property to the object (to be used with additionalProperties)
- `onAddClick: (schema: RJSFSchema) => () => void`: Returns a function that adds a new property to the object (to be used with additionalProperties and patternProperties)
- `readonly`: A boolean value stating if the object is read-only.
- `required`: A boolean value stating if the object is required.
- `hideError`: A boolean value stating if the field is hiding its errors.
Expand Down Expand Up @@ -908,8 +908,8 @@ The following props are passed to each `UnsupportedFieldTemplate`:

## WrapIfAdditionalTemplate

The `WrapIfAdditionalTemplate` is used by the `FieldTemplate` to conditionally render additional controls if `additionalProperties` is present in the schema.
You may customize `WrapIfAdditionalTemplate` if you wish to change the layout or behavior of user-controlled `additionalProperties`.
The `WrapIfAdditionalTemplate` is used by the `FieldTemplate` to conditionally render additional controls if `additionalProperties` or `patternProperties` are present in the schema.
You may customize `WrapIfAdditionalTemplate` if you wish to change the layout or behavior of user-controlled `additionalProperties` and `patternProperties`.

```tsx
import { RJSFSchema, WrapIfAdditionalTemplateProps } from '@rjsf/utils';
Expand Down Expand Up @@ -987,7 +987,7 @@ Each button template (except for the `SubmitButton`) accepts, as props, the stan

### AddButton

The `AddButton` is used to render an add action on a `Form` for both a new `additionalProperties` element for an object or a new element in an array.
The `AddButton` is used to render an add action on a `Form` for both a new `additionalProperties` or `patternProperties` element for an object or a new element in an array.
You can customize the `AddButton` to render something other than the icon button that is provided by a theme as follows:

```tsx
Expand Down Expand Up @@ -1077,7 +1077,7 @@ render(

### RemoveButton

The `RemoveButton` is used to render a remove action on a `Form` for both a existing `additionalProperties` element for an object or an existing element in an array.
The `RemoveButton` is used to render a remove action on a `Form` for both a existing `additionalProperties` or `patternProperties` element for an object or an existing element in an array.
You can customize the `RemoveButton` to render something other than the icon button that is provided by a theme as follows:

```tsx
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/docs/api-reference/form-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ Sometimes you may want to trigger events or modify external state when a field h
If you plan on being notified every time the form data are updated, you can pass an `onChange` handler, which will receive the same first argument as `onSubmit` any time a value is updated in the form.
It will also receive, as the second argument, the `id` of the field which experienced the change.
Generally, this will be the `id` of the field for which input data is modified.
In the case of adding/removing of new fields in arrays or objects with `additionalProperties` and the rearranging of items in arrays, the `id` will be that of the array or object itself, rather than the item/field being added, removed or moved.
In the case of adding/removing of new fields in arrays or objects with `additionalProperties` or `patternProperties` and the rearranging of items in arrays, the `id` will be that of the array or object itself, rather than the item/field being added, removed or moved.

## onError

Expand Down
3 changes: 2 additions & 1 deletion packages/docs/docs/api-reference/utility-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ The UI for the field can expand if it has additional properties, is not forced a

#### Returns

- boolean: True if the schema element has additionalProperties, is expandable, and not at the maxProperties limit
- boolean: True if the schema element has additionalProperties or patternProperties keywords, is expandable, and not at the maxProperties limit

### createErrorHandler<T = any>()

Expand Down Expand Up @@ -392,6 +392,7 @@ If the type is not explicitly defined, then an attempt is made to infer it from
- schema.enum: Returns `string`
- schema.properties: Returns `object`
- schema.additionalProperties: Returns `object`
- schema.patternProperties: Returns `object`
- type is an array with a length of 2 and one type is 'null': Returns the other type

#### Parameters
Expand Down
31 changes: 29 additions & 2 deletions packages/docs/docs/json-schema/objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const uiSchema: UiSchema = {
};
```

## Additional properties
## Additional and pattern properties

The `additionalProperties` keyword allows the user to add properties with arbitrary key names. Set this keyword equal to a schema object:

Expand Down Expand Up @@ -116,9 +116,36 @@ In this way, an add button for new properties is shown by default.

You can also define `uiSchema` options for `additionalProperties` by setting the `additionalProperties` attribute in the `uiSchema`.

The `patternProperties` keyword allows the user to add properties with names that match one or more of the specified regular expressions

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

const schema: RJSFSchema = {
type: 'object',
properties: {
name: {
type: 'string',
},
},
patternProperties: {
'^foo+$': {
type: 'number',
enum: [1, 2, 3],
},
},
};

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

Also in this case, an add button for new properties is shown by default.

### `expandable` option

You can turn support for `additionalProperties` off with the `expandable` option in `uiSchema`:
You can turn support for `additionalProperties` and `patternProperties` off with the `expandable` option in `uiSchema`:

```ts
import { UiSchema } from '@rjsf/utils';
Expand Down
2 changes: 2 additions & 0 deletions packages/playground/src/samples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import customField from './customField';
import layoutGrid from './layoutGrid';
import { Sample } from './Sample';
import deepFreeze from 'deep-freeze-es6';
import patternProperties from './patternProperties';

export type { Sample };

Expand Down Expand Up @@ -61,6 +62,7 @@ const _samples: Record<string, Sample> = {
'Property dependencies': propertyDependencies,
'Schema dependencies': schemaDependencies,
'Additional Properties': additionalProperties,
'Pattern Properties': patternProperties,
'Any Of': anyOf,
'Any Of with Custom Field': customFieldAnyOf,
'One Of': oneOf,
Expand Down
38 changes: 38 additions & 0 deletions packages/playground/src/samples/patternProperties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Sample } from './Sample';

const patternProperties: Sample = {
schema: {
title: 'A customizable registration form',
description: 'A simple form with pattern properties example.',
type: 'object',
required: ['firstName', 'lastName'],
properties: {
firstName: {
type: 'string',
title: 'First name',
},
lastName: {
type: 'string',
title: 'Last name',
},
},
patternProperties: {
'^[a-z][a-zA-Z]+$': {
type: 'string',
},
},
},
uiSchema: {
firstName: {
'ui:autofocus': true,
'ui:emptyValue': '',
},
},
formData: {
firstName: 'Chuck',
lastName: 'Norris',
assKickCount: 'infinity',
},
};

export default patternProperties;
2 changes: 1 addition & 1 deletion packages/utils/src/canExpand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function canExpand<T = any, S extends StrictRJSFSchema = RJSFSche
uiSchema: UiSchema<T, S, F> = {},
formData?: T,
) {
if (!schema.additionalProperties) {
if (!(schema.additionalProperties || schema.patternProperties)) {
return false;
}
const { expandable = true } = getUiOptions<T, S, F>(uiSchema);
Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const ITEMS_KEY = 'items';
export const JUNK_OPTION_ID = '_$junk_option_schema_id$_';
export const NAME_KEY = '$name';
export const ONE_OF_KEY = 'oneOf';
export const PATTERN_PROPERTIES_KEY = 'patternProperties';
export const PROPERTIES_KEY = 'properties';
export const READONLY_KEY = 'readonly';
export const REQUIRED_KEY = 'required';
Expand Down
3 changes: 2 additions & 1 deletion packages/utils/src/getSchemaType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RJSFSchema, StrictRJSFSchema } from './types';
* - schema.enum: Returns `string`
* - schema.properties: Returns `object`
* - schema.additionalProperties: Returns `object`
* - schema.patternproperties: Returns `object`
* - type is an array with a length of 2 and one type is 'null': Returns the other type
*
* @param schema - The schema for which to get the type
Expand All @@ -25,7 +26,7 @@ export default function getSchemaType<S extends StrictRJSFSchema = RJSFSchema>(
return 'string';
}

if (!type && (schema.properties || schema.additionalProperties)) {
if (!type && (schema.properties || schema.additionalProperties || schema.patternProperties)) {
return 'object';
}

Expand Down
Loading