From 7d3f53f82b9e8bc1f6918afeae136837710a4a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Menezes?= Date: Thu, 24 Jul 2025 17:31:50 -0300 Subject: [PATCH 1/6] Expose title property for validated fields --- packages/utils/src/types.ts | 2 ++ packages/validator-ajv8/src/processRawValidationErrors.ts | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 411bc741c7..b80f46ad92 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -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 */ diff --git a/packages/validator-ajv8/src/processRawValidationErrors.ts b/packages/validator-ajv8/src/processRawValidationErrors.ts index 38b2c41225..9906f8e43a 100644 --- a/packages/validator-ajv8/src/processRawValidationErrors.ts +++ b/packages/validator-ajv8/src/processRawValidationErrors.ts @@ -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, @@ -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; } } }); @@ -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; } } } @@ -97,6 +102,7 @@ export function transformRJSFValidationErrors< params, // specific to ajv stack, schemaPath, + title: uiTitle, }; }); } From aec16b7ce785edb33dbb47ab916199e3a4c6c981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Menezes?= Date: Thu, 24 Jul 2025 17:32:24 -0300 Subject: [PATCH 2/6] Update docs and examples with new exposed title property --- packages/docs/docs/usage/validation.md | 1 + packages/playground/src/samples/validation.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/packages/docs/docs/usage/validation.md b/packages/docs/docs/usage/validation.md index 969be491cf..725853d213 100644 --- a/packages/docs/docs/usage/validation.md +++ b/packages/docs/docs/usage/validation.md @@ -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 the field with the error title property. ## Error List Display diff --git a/packages/playground/src/samples/validation.ts b/packages/playground/src/samples/validation.ts index adfd06b020..3bd283bc61 100644 --- a/packages/playground/src/samples/validation.ts +++ b/packages/playground/src/samples/validation.ts @@ -15,6 +15,12 @@ const transformErrors: ErrorTransformer = (errors) => { message: 'You need to be 18 because of some legal thing', }); } + if (error.name === 'required') { + debugger + return Object.assign({}, error, { + message: `${error.title} is a required field` + }) + } return error; }); }; @@ -25,7 +31,14 @@ 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', From f704a7d22b7eb8f282c369969db4b7463e9b8504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Menezes?= Date: Thu, 24 Jul 2025 18:09:29 -0300 Subject: [PATCH 3/6] Change documentation entry wording --- packages/docs/docs/usage/validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs/docs/usage/validation.md b/packages/docs/docs/usage/validation.md index 725853d213..a251eaa812 100644 --- a/packages/docs/docs/usage/validation.md +++ b/packages/docs/docs/usage/validation.md @@ -382,7 +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 the field with the error title property. +- `title`: optional string containing title property for the field with error. ## Error List Display From 46d211a52e30fb79f0c3d32f59057c3145c2f3e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Menezes?= Date: Thu, 24 Jul 2025 18:12:38 -0300 Subject: [PATCH 4/6] Remove missed debugger statement in playground --- packages/playground/src/samples/validation.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/playground/src/samples/validation.ts b/packages/playground/src/samples/validation.ts index 3bd283bc61..8f87096c4a 100644 --- a/packages/playground/src/samples/validation.ts +++ b/packages/playground/src/samples/validation.ts @@ -16,10 +16,9 @@ const transformErrors: ErrorTransformer = (errors) => { }); } if (error.name === 'required') { - debugger return Object.assign({}, error, { - message: `${error.title} is a required field` - }) + message: `${error.title} is a required field`, + }); } return error; }); @@ -31,13 +30,11 @@ 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' - ], + required: ['firstName'], properties: { firstName: { title: 'First Name', - type: 'string' + type: 'string', }, pass1: { title: 'Password', From f31f7bc7d935f1e0d3277c7d69133a2139ea0c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Menezes?= Date: Tue, 29 Jul 2025 21:58:27 -0300 Subject: [PATCH 5/6] Fix unit tests and update CHANGELOG.md --- CHANGELOG.md | 5 +++++ packages/core/test/ArrayField.test.jsx | 3 +++ packages/core/test/Form.test.jsx | 20 ++++++++++++++++++++ packages/core/test/ObjectField.test.jsx | 1 + packages/core/test/StringField.test.jsx | 7 +++++++ packages/core/test/validate.test.js | 5 +++++ 6 files changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6be34694d5..2a7edabce6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,11 @@ should change the heading of the (upcoming) version to include a major version b ## @rjsf/utils - Updated `SchemaUtils` and `createSchemaUtils()` to add a new `getRootSchema()` function +- Added `title` property to `RJSFValidationError` [PR](https://github.com/rjsf-team/react-jsonschema-form/pull/4700) + +## @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.11 diff --git a/packages/core/test/ArrayField.test.jsx b/packages/core/test/ArrayField.test.jsx index 6794e23272..5d69881d5c 100644 --- a/packages/core/test/ArrayField.test.jsx +++ b/packages/core/test/ArrayField.test.jsx @@ -1074,6 +1074,7 @@ describe('ArrayField', () => { property: '.1', schemaPath: '#/items/type', stack: '.1 must be integer', + title: '', }, ], formData: [1, null, 3], @@ -1090,6 +1091,7 @@ describe('ArrayField', () => { property: '.1', schemaPath: '#/items/type', stack: '.1 must be integer', + title: '', }, ]); }); @@ -1291,6 +1293,7 @@ describe('ArrayField', () => { property: '.multipleChoicesList', schemaPath: '#/properties/multipleChoicesList/minItems', stack: '.multipleChoicesList must NOT have fewer than 3 items', + title: '', }, ]); }); diff --git a/packages/core/test/Form.test.jsx b/packages/core/test/Form.test.jsx index 42623dc5fb..2c047642b0 100644 --- a/packages/core/test/Form.test.jsx +++ b/packages/core/test/Form.test.jsx @@ -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'", @@ -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'", @@ -1796,6 +1798,7 @@ describeRepeated('Form common', (createFormComponent) => { property: '.shipping_address.state', schemaPath: '#/properties/shipping_address/required', stack: "must have required property 'state'", + title: '', }, ]); }); @@ -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', }, ]); }); @@ -2061,6 +2065,7 @@ describeRepeated('Form common', (createFormComponent) => { property: '', schemaPath: '#/type', stack: 'must be number', + title: '', }, ]); }); @@ -2544,6 +2549,7 @@ describeRepeated('Form common', (createFormComponent) => { property: '', schemaPath: '#/minLength', stack: 'must NOT have fewer than 8 characters', + title: '', }, ]); sinon.assert.calledOnce(onError); @@ -2606,6 +2612,7 @@ describeRepeated('Form common', (createFormComponent) => { property: '', schemaPath: '#/minLength', stack: 'must NOT have fewer than 8 characters', + title: '', }, ]); }); @@ -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+"', @@ -2650,6 +2658,7 @@ describeRepeated('Form common', (createFormComponent) => { property: '', schemaPath: '#/pattern', stack: 'must match pattern "d+"', + title: '', }, ]); }); @@ -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: '', }, ]); }); @@ -2742,6 +2752,7 @@ describeRepeated('Form common', (createFormComponent) => { property: '.1', schemaPath: '#/items/minLength', stack: '.1 must NOT have fewer than 4 characters', + title: '', }, ]); }); @@ -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', @@ -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: '', }, ]); }); @@ -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', @@ -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); @@ -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: '', }, ]); }); @@ -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. @@ -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+"', @@ -3614,6 +3632,7 @@ describeRepeated('Form common', (createFormComponent) => { property: '', schemaPath: '#/pattern', stack: 'must match pattern "d+"', + title: '', }, ]); }); @@ -4648,6 +4667,7 @@ describe('Form omitExtraData and liveOmit', () => { }, schemaPath: '#/required', stack: "must have required property 'foo'", + title: '', }, ]); diff --git a/packages/core/test/ObjectField.test.jsx b/packages/core/test/ObjectField.test.jsx index 6424aba07c..596d83025a 100644 --- a/packages/core/test/ObjectField.test.jsx +++ b/packages/core/test/ObjectField.test.jsx @@ -714,6 +714,7 @@ describe('ObjectField', () => { property: '', schemaPath: '#/additionalProperties', stack: 'must NOT have additional properties', + title: '', }, ]); }); diff --git a/packages/core/test/StringField.test.jsx b/packages/core/test/StringField.test.jsx index 20c7bc2028..1d5cacc1e7 100644 --- a/packages/core/test/StringField.test.jsx +++ b/packages/core/test/StringField.test.jsx @@ -808,6 +808,7 @@ describe('StringField', () => { property: '', schemaPath: '#/type', stack: 'must be string', + title: '', }, ], }); @@ -843,6 +844,7 @@ describe('StringField', () => { property: '', schemaPath: '#/type', stack: 'must be string', + title: '', }, ], }); @@ -1010,6 +1012,7 @@ describe('StringField', () => { property: '', schemaPath: '#/format', stack: 'must match format "date"', + title: '', }, ], }); @@ -1141,6 +1144,7 @@ describe('StringField', () => { property: '', schemaPath: '#/format', stack: 'must match format "time"', + title: '', }, ], }); @@ -1948,6 +1952,7 @@ describe('StringField', () => { property: '', schemaPath: '#/format', stack: 'must match format "email"', + title: '', }, ], }); @@ -2088,6 +2093,7 @@ describe('StringField', () => { property: '', schemaPath: '#/format', stack: 'must match format "uri"', + title: '', }, ], }); @@ -2209,6 +2215,7 @@ describe('StringField', () => { property: '', schemaPath: '#/format', stack: 'must match format "color"', + title: '', }, ], }); diff --git a/packages/core/test/validate.test.js b/packages/core/test/validate.test.js index 9b168ade6a..3f51e0ed12 100644 --- a/packages/core/test/validate.test.js +++ b/packages/core/test/validate.test.js @@ -50,6 +50,7 @@ describe('Validation', () => { property: 'foo', schemaPath: '#/required', stack: "must have required property 'foo'", + title: '', }, ]); }); @@ -102,6 +103,7 @@ describe('Validation', () => { property: '.foo', schemaPath: '#/properties/foo/minLength', stack: '.foo must NOT have fewer than 10 characters', + title: '', }, ]); }); @@ -249,6 +251,7 @@ describe('Validation', () => { property: '.pass2', schemaPath: '#/properties/pass2/minLength', stack: '.pass2 must NOT have fewer than 3 characters', + title: '', }, { property: '.pass2', @@ -372,6 +375,7 @@ describe('Validation', () => { property: 'foo', schemaPath: '#/required', stack: "must have required property 'foo'", + title: '', }, ]); }); @@ -467,6 +471,7 @@ describe('Validation', () => { property: '.datasetId', schemaPath: '#/properties/datasetId/pattern', stack: '.datasetId must match pattern "\\d+"', + title: '', }, ]); onError.resetHistory(); From f8123e52e260dd974c1182f1d57d4c4a4a46167a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Menezes?= Date: Tue, 12 Aug 2025 15:42:46 -0300 Subject: [PATCH 6/6] Update changelog --- CHANGELOG.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7edabce6..95c1ebdc76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,16 @@ 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/utils + +- Added `title` property to `RJSFValidationError` [PR](https://github.com/rjsf-team/react-jsonschema-form/pull/4700) + +## @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 @@ -52,11 +62,6 @@ should change the heading of the (upcoming) version to include a major version b ## @rjsf/utils - Updated `SchemaUtils` and `createSchemaUtils()` to add a new `getRootSchema()` function -- Added `title` property to `RJSFValidationError` [PR](https://github.com/rjsf-team/react-jsonschema-form/pull/4700) - -## @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.11