Skip to content

Commit

Permalink
Fixed issue where ui:title in anyOf/oneOf is not shown in error messa…
Browse files Browse the repository at this point in the history
…ges. Fixes #4368.
  • Loading branch information
chibacchie committed Dec 3, 2024
1 parent 433f99b commit 7676d0d
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ should change the heading of the (upcoming) version to include a major version b

- Fix default value population when switching between options in `MultiSchemaField` [#4375](https://github.com/rjsf-team/react-jsonschema-form/pull/4375). Fixes [#4367](https://github.com/rjsf-team/react-jsonschema-form/issues/4367)

## @rjsf/validator-ajv8

- Fixed issue where `ui:title` in anyOf/oneOf is not shown in error messages. Fixes [#4368](https://github.com/rjsf-team/react-jsonschema-form/issues/4368)

# 5.23.1

## @rjsf/chakra-ui
Expand Down
10 changes: 9 additions & 1 deletion packages/validator-ajv8/src/processRawValidationErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,15 @@ export function transformRJSFValidationErrors<
if ('missingProperty' in params) {
property = property ? `${property}.${params.missingProperty}` : params.missingProperty;
const currentProperty: string = params.missingProperty;
const uiSchemaTitle = getUiOptions(get(uiSchema, `${property.replace(/^\./, '')}`)).title;
let uiSchemaTitle = getUiOptions(get(uiSchema, `${property.replace(/^\./, '')}`)).title;
if (uiSchemaTitle === undefined) {
const uiSchemaPath = schemaPath
.replaceAll('/properties/', '/')
.split('/')
.slice(1, -1)
.concat([currentProperty]);
uiSchemaTitle = getUiOptions(get(uiSchema, uiSchemaPath)).title;
}

if (uiSchemaTitle) {
message = message.replace(`'${currentProperty}'`, `'${uiSchemaTitle}'`);
Expand Down
241 changes: 241 additions & 0 deletions packages/validator-ajv8/test/validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,247 @@ describe('AJV8Validator', () => {
expect(errorSchema.numberOfChildren!.__errors![0]).toEqual('must match pattern "\\d+"');
});
});
describe('title is in validation message when it is in the uiSchema ui:title field with anyOf', () => {
beforeAll(() => {
const schema: RJSFSchema = {
anyOf: [
{
type: 'object',
required: ['firstName', 'lastName'],
properties: {
firstName: { type: 'string', title: 'First Name' },
lastName: { type: 'string', title: 'Last Name' },
},
},
],
};
const uiSchema: UiSchema = {
anyOf: [
{
firstName: {
'ui:title': 'uiSchema First Name',
},
lastName: {
'ui:title': 'uiSchema Last Name',
},
},
],
};

const formData = { firstName: 'a' };
const result = validator.validateFormData(formData, schema, undefined, undefined, uiSchema);
errors = result.errors;
errorSchema = result.errorSchema;
});
it('should return an error list', () => {
expect(errors).toHaveLength(2);

const expected = ["must have required property 'uiSchema Last Name'", 'must match a schema in anyOf'];

const messages = errors.map((e) => e.message);
expect(messages).toEqual(expected);

const stack = errors.map((e) => e.stack);
expect(stack).toEqual(expected);
});
it('should return an errorSchema', () => {
expect(errorSchema.lastName!.__errors).toHaveLength(1);
expect(errorSchema.lastName!.__errors![0]).toEqual("must have required property 'uiSchema Last Name'");

expect(errorSchema.__errors).toHaveLength(1);
expect(errorSchema.__errors![0]).toEqual('must match a schema in anyOf');
});
});
describe('title is in validation message when it is in the uiSchema ui:title field with oneOf', () => {
beforeAll(() => {
const schema: RJSFSchema = {
oneOf: [
{
type: 'object',
required: ['firstName', 'lastName'],
properties: {
firstName: { type: 'string', title: 'First Name' },
lastName: { type: 'string', title: 'Last Name' },
},
},
],
};
const uiSchema: UiSchema = {
oneOf: [
{
firstName: {
'ui:title': 'uiSchema First Name',
},
lastName: {
'ui:title': 'uiSchema Last Name',
},
},
],
};

const formData = { firstName: 'a' };
const result = validator.validateFormData(formData, schema, undefined, undefined, uiSchema);
errors = result.errors;
errorSchema = result.errorSchema;
});
it('should return an error list', () => {
expect(errors).toHaveLength(2);

const expected = [
"must have required property 'uiSchema Last Name'",
'must match exactly one schema in oneOf',
];

const messages = errors.map((e) => e.message);
expect(messages).toEqual(expected);

const stack = errors.map((e) => e.stack);
expect(stack).toEqual(expected);
});
it('should return an errorSchema', () => {
expect(errorSchema.lastName!.__errors).toHaveLength(1);
expect(errorSchema.lastName!.__errors![0]).toEqual("must have required property 'uiSchema Last Name'");

expect(errorSchema.__errors).toHaveLength(1);
expect(errorSchema.__errors![0]).toEqual('must match exactly one schema in oneOf');
});
});
describe('ui:title is in validation message when it is defined in referrer', () => {
beforeAll(() => {
const schema: RJSFSchema = {
definitions: {
address: {
type: 'object',
properties: {
streetAddress: { type: 'string' },
city: { type: 'string' },
state: { type: 'string' },
},
required: ['streetAddress', 'city', 'state'],
},
},
type: 'object',
required: ['billingAddress', 'shippingAddress'],
properties: {
billingAddress: {
$ref: '#/definitions/address',
},
shippingAddress: {
$ref: '#/definitions/address',
},
},
};
const uiSchema: UiSchema = {
billingAddress: {
'ui:title': 'uiSchema Billing Address',
},
shippingAddress: {
city: {
'ui:title': 'uiSchema City',
},
},
};

const formData = { shippingAddress: { streetAddress: 'El Camino Real', state: 'California' } };
const result = validator.validateFormData(formData, schema, undefined, undefined, uiSchema);
errors = result.errors;
errorSchema = result.errorSchema;
});
it('should return an error list', () => {
expect(errors).toHaveLength(2);

const expected = [
"must have required property 'uiSchema Billing Address'",
"must have required property 'uiSchema City'",
];

const messages = errors.map((e) => e.message);
expect(messages).toEqual(expected);

const stack = errors.map((e) => e.stack);
expect(stack).toEqual(expected);
});
it('should return an errorSchema', () => {
expect(errorSchema.billingAddress!.__errors).toHaveLength(1);
expect(errorSchema.billingAddress!.__errors![0]).toEqual(
"must have required property 'uiSchema Billing Address'"
);

expect(errorSchema.shippingAddress!.city!.__errors).toHaveLength(1);
expect(errorSchema.shippingAddress!.city!.__errors![0]).toEqual(
"must have required property 'uiSchema City'"
);
});
});
describe('ui:title is in validation message when it is defined in definitions', () => {
beforeAll(() => {
const schema: RJSFSchema = {
definitions: {
address: {
type: 'object',
properties: {
streetAddress: { type: 'string' },
city: { type: 'string' },
state: { type: 'string' },
},
required: ['streetAddress', 'city', 'state'],
},
},
type: 'object',
required: ['billingAddress', 'shippingAddress'],
properties: {
billingAddress: {
$ref: '#/definitions/address',
},
shippingAddress: {
$ref: '#/definitions/address',
},
},
};
const uiSchema: UiSchema = {
definitions: {
address: {
city: {
'ui:title': 'uiSchema City',
},
},
},
billingAddress: {
'ui:title': 'uiSchema Billing Address',
},
};

const formData = { shippingAddress: { streetAddress: 'El Camino Real', state: 'California' } };
const result = validator.validateFormData(formData, schema, undefined, undefined, uiSchema);
errors = result.errors;
errorSchema = result.errorSchema;
});
it('should return an error list', () => {
expect(errors).toHaveLength(2);

const expected = [
"must have required property 'uiSchema Billing Address'",
"must have required property 'uiSchema City'",
];

const messages = errors.map((e) => e.message);
expect(messages).toEqual(expected);

const stack = errors.map((e) => e.stack);
expect(stack).toEqual(expected);
});
it('should return an errorSchema', () => {
expect(errorSchema.billingAddress!.__errors).toHaveLength(1);
expect(errorSchema.billingAddress!.__errors![0]).toEqual(
"must have required property 'uiSchema Billing Address'"
);

expect(errorSchema.shippingAddress!.city!.__errors).toHaveLength(1);
expect(errorSchema.shippingAddress!.city!.__errors![0]).toEqual(
"must have required property 'uiSchema City'"
);
});
});
describe('uiSchema title in validation when defined in nested field', () => {
beforeAll(() => {
const schema: RJSFSchema = {
Expand Down

0 comments on commit 7676d0d

Please sign in to comment.