fix(ui): json schema form field validation (#11867)

* fix(ui): json schema form field validation

* address comment

* fix: duplicate key issue

* add unit test
This commit is contained in:
Sachin Chaurasiya 2023-06-05 13:52:00 +05:30 committed by GitHub
parent ce9a10dd32
commit cec1c295a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 1 deletions

View File

@ -0,0 +1,64 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FieldErrorProps } from '@rjsf/utils';
import { render } from '@testing-library/react';
import React from 'react';
import { FieldErrorTemplate } from './FieldErrorTemplate';
describe('FieldErrorTemplate', () => {
it('renders error list correctly', () => {
const errors = ['Error 1', 'Error 2'];
const schema = { $id: 'schema-id' };
const idSchema = { $id: 'id-schema-id' };
const { container } = render(
<FieldErrorTemplate
errors={errors}
idSchema={idSchema as FieldErrorProps['idSchema']}
registry={{} as FieldErrorProps['registry']}
schema={schema}
/>
);
const errorItems = container.querySelectorAll(
'.ant-form-item-explain-error'
);
expect(errorItems).toHaveLength(errors.length);
errorItems.forEach((item, index) => {
expect(item.textContent).toBe(errors[index]);
});
});
it('renders null when errors are empty', () => {
const errors: string[] = [];
const schema = { $id: 'schema-id' };
const idSchema = { $id: 'id-schema-id' };
const { container } = render(
<FieldErrorTemplate
errors={errors}
idSchema={idSchema as FieldErrorProps['idSchema']}
registry={{} as FieldErrorProps['registry']}
schema={schema}
/>
);
const errorItems = container.querySelectorAll(
'.ant-form-item-explain-error'
);
expect(errorItems).toHaveLength(0);
});
});

View File

@ -0,0 +1,37 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FieldErrorProps } from '@rjsf/utils';
import { isEmpty } from 'lodash';
import React, { FC } from 'react';
export const FieldErrorTemplate: FC<FieldErrorProps> = (props) => {
const errorList = [...new Set(props.errors ?? [])];
if (isEmpty(errorList)) {
return null;
}
return (
<div>
<ul>
{errorList.map((error) => (
<li
className="ant-form-item-explain-error"
key={`${props.schema.$id}-${props.idSchema.$id}`}>
{error}
</li>
))}
</ul>
</div>
);
};

View File

@ -17,6 +17,7 @@ import { Button } from 'antd';
import classNames from 'classnames';
import { ArrayFieldTemplate } from 'components/JSONSchemaTemplate/ArrayFieldTemplate';
import DescriptionFieldTemplate from 'components/JSONSchemaTemplate/DescriptionFieldTemplate';
import { FieldErrorTemplate } from 'components/JSONSchemaTemplate/FieldErrorTemplate/FieldErrorTemplate';
import { ObjectFieldTemplate } from 'components/JSONSchemaTemplate/ObjectFieldTemplate';
import PasswordWidget from 'components/JsonSchemaWidgets/PasswordWidget';
import { ServiceCategory } from 'enums/service.enum';
@ -123,6 +124,7 @@ const FormBuilder: FunctionComponent<Props> = ({
ArrayFieldTemplate: ArrayFieldTemplate,
ObjectFieldTemplate: ObjectFieldTemplate,
DescriptionFieldTemplate: DescriptionFieldTemplate,
FieldErrorTemplate: FieldErrorTemplate,
}}
transformErrors={transformErrors}
uiSchema={uiSchema}

View File

@ -192,7 +192,15 @@ export const generateFormFields = (fields: FieldProp[]) => {
export const transformErrors: ErrorTransformer = (errors) => {
const errorRet = errors.map((error) => {
const { property } = error;
const id = 'root' + property?.replaceAll('.', '/');
/**
* For nested fields we have to check if it's property start with "."
* else we will just prepend the root to property
*/
const id = property?.startsWith('.')
? 'root' + property?.replaceAll('.', '/')
: `root/${property}`;
// If element is not present in DOM, ignore error
if (document.getElementById(id)) {
const fieldName = error.params?.missingProperty;