Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ it according to semantic versioning. For example, if your PR adds a breaking cha
should change the heading of the (upcoming) version to include a major version bump.

-->

# 6.0.0-beta.14

## @rjsf/core
Expand All @@ -26,6 +25,7 @@ should change the heading of the (upcoming) version to include a major version b
## @rjsf/utils

- Updated `UiSchema` type to support dynamic array item UI schemas - the `items` property can now be either a `UiSchema` object or a function that returns a `UiSchema` ([#4706](https://github.com/rjsf-team/react-jsonschema-form/pull/4706))
- Added `title` property to `RJSFValidationError` [PR](https://github.com/rjsf-team/react-jsonschema-form/pull/4700)

## @rjsf/chakra-ui

Expand Down Expand Up @@ -56,6 +56,10 @@ should change the heading of the (upcoming) version to include a major version b
- Added comprehensive documentation for dynamic UI schema feature with TypeScript examples ([#4706](https://github.com/rjsf-team/react-jsonschema-form/pull/4706))
- Updated array documentation to reference the new dynamic UI schema capabilities ([#4706](https://github.com/rjsf-team/react-jsonschema-form/pull/4706))

## @rjsf/validator-ajv8

- Updated `transformRJSFValidationErrors()` to include the `title` property of a field with error fixing #4504 with [PR](https://github.com/rjsf-team/react-jsonschema-form/pull/4700)

# 6.0.0-beta.13

## @rjsf/shadcn
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/ArrayField.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,7 @@ describe('ArrayField', () => {
property: '.1',
schemaPath: '#/items/type',
stack: '.1 must be integer',
title: '',
},
],
formData: [1, null, 3],
Expand All @@ -1090,6 +1091,7 @@ describe('ArrayField', () => {
property: '.1',
schemaPath: '#/items/type',
stack: '.1 must be integer',
title: '',
},
]);
});
Expand Down Expand Up @@ -1291,6 +1293,7 @@ describe('ArrayField', () => {
property: '.multipleChoicesList',
schemaPath: '#/properties/multipleChoicesList/minItems',
stack: '.multipleChoicesList must NOT have fewer than 3 items',
title: '',
},
]);
});
Expand Down
20 changes: 20 additions & 0 deletions packages/core/test/Form.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.shipping_address.street_address',
schemaPath: '#/properties/shipping_address/required',
stack: "must have required property 'street_address'",
title: '',
},
{
message: "must have required property 'city'",
Expand All @@ -1788,6 +1789,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.shipping_address.city',
schemaPath: '#/properties/shipping_address/required',
stack: "must have required property 'city'",
title: '',
},
{
message: "must have required property 'state'",
Expand All @@ -1796,6 +1798,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.shipping_address.state',
schemaPath: '#/properties/shipping_address/required',
stack: "must have required property 'state'",
title: '',
},
]);
});
Expand Down Expand Up @@ -1852,6 +1855,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.albums',
schemaPath: '#/properties/albums/minItems',
stack: "'Album Titles' must NOT have fewer than 3 items",
title: 'Album Titles',
},
]);
});
Expand Down Expand Up @@ -2061,6 +2065,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '',
schemaPath: '#/type',
stack: 'must be number',
title: '',
},
]);
});
Expand Down Expand Up @@ -2544,6 +2549,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '',
schemaPath: '#/minLength',
stack: 'must NOT have fewer than 8 characters',
title: '',
},
]);
sinon.assert.calledOnce(onError);
Expand Down Expand Up @@ -2606,6 +2612,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '',
schemaPath: '#/minLength',
stack: 'must NOT have fewer than 8 characters',
title: '',
},
]);
});
Expand Down Expand Up @@ -2642,6 +2649,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '',
schemaPath: '#/minLength',
stack: 'must NOT have fewer than 8 characters',
title: '',
},
{
message: 'must match pattern "d+"',
Expand All @@ -2650,6 +2658,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '',
schemaPath: '#/pattern',
stack: 'must match pattern "d+"',
title: '',
},
]);
});
Expand Down Expand Up @@ -2702,6 +2711,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.level1.level2',
schemaPath: '#/properties/level1/properties/level2/minLength',
stack: '.level1.level2 must NOT have fewer than 8 characters',
title: '',
},
]);
});
Expand Down Expand Up @@ -2742,6 +2752,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.1',
schemaPath: '#/items/minLength',
stack: '.1 must NOT have fewer than 4 characters',
title: '',
},
]);
});
Expand Down Expand Up @@ -2798,6 +2809,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.level1.1',
schemaPath: '#/properties/level1/items/minLength',
stack: '.level1.1 must NOT have fewer than 4 characters',
title: '',
},
{
message: 'must NOT have fewer than 4 characters',
Expand All @@ -2806,6 +2818,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.level1.3',
schemaPath: '#/properties/level1/items/minLength',
stack: '.level1.3 must NOT have fewer than 4 characters',
title: '',
},
]);
});
Expand Down Expand Up @@ -2871,6 +2884,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.outer.0.1',
schemaPath: '#/properties/outer/items/items/minLength',
stack: '.outer.0.1 must NOT have fewer than 4 characters',
title: '',
},
{
message: 'must NOT have fewer than 4 characters',
Expand All @@ -2879,6 +2893,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.outer.1.0',
schemaPath: '#/properties/outer/items/items/minLength',
stack: '.outer.1.0 must NOT have fewer than 4 characters',
title: '',
},
]);
sinon.assert.calledOnce(focusSpy);
Expand Down Expand Up @@ -2933,6 +2948,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.1.foo',
schemaPath: '#/items/properties/foo/minLength',
stack: '.1.foo must NOT have fewer than 4 characters',
title: '',
},
]);
});
Expand Down Expand Up @@ -3558,6 +3574,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '.areaCode',
schemaPath: '#/properties/areaCode/format',
stack: '.areaCode must match format "area-code"',
title: '',
},
]);
// We use setTimeout with a delay of 0ms to allow all asynchronous operations to complete in the React component.
Expand Down Expand Up @@ -3606,6 +3623,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '',
schemaPath: '#/minLength',
stack: 'must NOT have fewer than 8 characters',
title: '',
},
{
message: 'must match pattern "d+"',
Expand All @@ -3614,6 +3632,7 @@ describeRepeated('Form common', (createFormComponent) => {
property: '',
schemaPath: '#/pattern',
stack: 'must match pattern "d+"',
title: '',
},
]);
});
Expand Down Expand Up @@ -4648,6 +4667,7 @@ describe('Form omitExtraData and liveOmit', () => {
},
schemaPath: '#/required',
stack: "must have required property 'foo'",
title: '',
},
]);

Expand Down
1 change: 1 addition & 0 deletions packages/core/test/ObjectField.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ describe('ObjectField', () => {
property: '',
schemaPath: '#/additionalProperties',
stack: 'must NOT have additional properties',
title: '',
},
]);
});
Expand Down
7 changes: 7 additions & 0 deletions packages/core/test/StringField.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ describe('StringField', () => {
property: '',
schemaPath: '#/type',
stack: 'must be string',
title: '',
},
],
});
Expand Down Expand Up @@ -843,6 +844,7 @@ describe('StringField', () => {
property: '',
schemaPath: '#/type',
stack: 'must be string',
title: '',
},
],
});
Expand Down Expand Up @@ -1010,6 +1012,7 @@ describe('StringField', () => {
property: '',
schemaPath: '#/format',
stack: 'must match format "date"',
title: '',
},
],
});
Expand Down Expand Up @@ -1141,6 +1144,7 @@ describe('StringField', () => {
property: '',
schemaPath: '#/format',
stack: 'must match format "time"',
title: '',
},
],
});
Expand Down Expand Up @@ -1948,6 +1952,7 @@ describe('StringField', () => {
property: '',
schemaPath: '#/format',
stack: 'must match format "email"',
title: '',
},
],
});
Expand Down Expand Up @@ -2088,6 +2093,7 @@ describe('StringField', () => {
property: '',
schemaPath: '#/format',
stack: 'must match format "uri"',
title: '',
},
],
});
Expand Down Expand Up @@ -2209,6 +2215,7 @@ describe('StringField', () => {
property: '',
schemaPath: '#/format',
stack: 'must match format "color"',
title: '',
},
],
});
Expand Down
5 changes: 5 additions & 0 deletions packages/core/test/validate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe('Validation', () => {
property: 'foo',
schemaPath: '#/required',
stack: "must have required property 'foo'",
title: '',
},
]);
});
Expand Down Expand Up @@ -102,6 +103,7 @@ describe('Validation', () => {
property: '.foo',
schemaPath: '#/properties/foo/minLength',
stack: '.foo must NOT have fewer than 10 characters',
title: '',
},
]);
});
Expand Down Expand Up @@ -249,6 +251,7 @@ describe('Validation', () => {
property: '.pass2',
schemaPath: '#/properties/pass2/minLength',
stack: '.pass2 must NOT have fewer than 3 characters',
title: '',
},
{
property: '.pass2',
Expand Down Expand Up @@ -372,6 +375,7 @@ describe('Validation', () => {
property: 'foo',
schemaPath: '#/required',
stack: "must have required property 'foo'",
title: '',
},
]);
});
Expand Down Expand Up @@ -467,6 +471,7 @@ describe('Validation', () => {
property: '.datasetId',
schemaPath: '#/properties/datasetId/pattern',
stack: '.datasetId must match pattern "\\d+"',
title: '',
},
]);
onError.resetHistory();
Expand Down
1 change: 1 addition & 0 deletions packages/docs/docs/usage/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ Each element in the `errors` list passed to `transformErrors` is a `RJSFValidati
- `property`: optional string in Javascript property accessor notation to the data path of the field with the error. For example, `.name` or `.first-name`.
- `schemaPath`: optional JSON pointer to the schema of the keyword that failed validation. For example, `#/fields/firstName/required`. (Note: this may sometimes be wrong due to a [bug in ajv](https://github.com/ajv-validator/ajv/issues/512)).
- `stack`: full error name, for example ".name is a required property".
- `title`: optional string containing title property for the field with error.

## Error List Display

Expand Down
10 changes: 10 additions & 0 deletions packages/playground/src/samples/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ const transformErrors: ErrorTransformer = (errors) => {
message: 'You need to be 18 because of some legal thing',
});
}
if (error.name === 'required') {
return Object.assign({}, error, {
message: `${error.title} is a required field`,
});
}
return error;
});
};
Expand All @@ -25,7 +30,12 @@ const validation: Sample = {
description:
'This form defines custom validation rules checking that the two passwords match. There is also a custom validation message when submitting an age < 18, which can only be seen if HTML5 validation is turned off.',
type: 'object',
required: ['firstName'],
properties: {
firstName: {
title: 'First Name',
type: 'string',
},
pass1: {
title: 'Password',
type: 'string',
Expand Down
2 changes: 2 additions & 0 deletions packages/utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ export type RJSFValidationError = {
schemaPath?: string;
/** Full error name, for example ".name is a required property" */
stack: string;
/** The title property for the failing field*/
title?: string;
};

/** The type that describes an error in a field */
Expand Down
6 changes: 6 additions & 0 deletions packages/validator-ajv8/src/processRawValidationErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function transformRJSFValidationErrors<
let { message = '' } = rest;
let property = instancePath.replace(/\//g, '.');
let stack = `${property} ${message}`.trim();
let uiTitle = '';
const rawPropertyNames: string[] = [
...(params.deps?.split(', ') || []),
params.missingProperty,
Expand All @@ -61,10 +62,12 @@ export function transformRJSFValidationErrors<
}
if (uiSchemaTitle) {
message = message.replace(`'${currentProperty}'`, `'${uiSchemaTitle}'`);
uiTitle = uiSchemaTitle;
} else {
const parentSchemaTitle = get(parentSchema, [PROPERTIES_KEY, currentProperty, 'title']);
if (parentSchemaTitle) {
message = message.replace(`'${currentProperty}'`, `'${parentSchemaTitle}'`);
uiTitle = parentSchemaTitle;
}
}
});
Expand All @@ -75,11 +78,13 @@ export function transformRJSFValidationErrors<

if (uiSchemaTitle) {
stack = `'${uiSchemaTitle}' ${message}`.trim();
uiTitle = uiSchemaTitle;
} else {
const parentSchemaTitle = parentSchema?.title;

if (parentSchemaTitle) {
stack = `'${parentSchemaTitle}' ${message}`.trim();
uiTitle = parentSchemaTitle;
}
}
}
Expand All @@ -97,6 +102,7 @@ export function transformRJSFValidationErrors<
params, // specific to ajv
stack,
schemaPath,
title: uiTitle,
Copy link
Member

@heath-freenome heath-freenome Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix... can you add a unit test in utils to verify this fix? And update the CHANGELOG.md as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I'll try getting something done in the next few days.

};
});
}
Expand Down