Improve validation error display text in scaffolder UI

Signed-off-by: Stephen Glass <stephen@stephen.glass>
This commit is contained in:
Stephen Glass
2024-06-19 22:14:49 -04:00
parent d57ebbca65
commit 354e68c953
3 changed files with 109 additions and 21 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-react': minor
---
Improve validation error display text in scaffolder
@@ -19,25 +19,88 @@ import { renderInTestApp } from '@backstage/test-utils';
import { ErrorListProps } from '@rjsf/utils';
describe('Error List Template', () => {
const errorList = {
errors: [
{
stack: 'Test error 1',
},
{
stack: 'Test error 2',
},
],
errorSchema: {},
} as Partial<ErrorListProps> as ErrorListProps;
describe('should render the error messages', () => {
it('no properties', async () => {
const errorList = {
errors: [
{
stack: 'Test error 1',
},
{
stack: 'Test error 2',
},
],
errorSchema: {},
} as Partial<ErrorListProps> as ErrorListProps;
it('should render the error messages', async () => {
const { getByText } = await renderInTestApp(
<ErrorListTemplate {...errorList} />,
);
const { getByText } = await renderInTestApp(
<ErrorListTemplate {...errorList} />,
);
for (const error of errorList.errors) {
expect(getByText(error.stack)).toBeInTheDocument();
}
for (const error of errorList.errors) {
expect(getByText(error.stack)).toBeInTheDocument();
}
});
it('properties no title', async () => {
const errorList = {
errors: [
{
property: '.foo',
stack: 'Test error 1',
message: 'Test error 1',
},
{
property: '.anExampleTitle',
stack: 'Test error 2',
message: 'Test error 2',
},
],
errorSchema: {},
schema: {},
} as Partial<ErrorListProps> as ErrorListProps;
const { getByText } = await renderInTestApp(
<ErrorListTemplate {...errorList} />,
);
expect(getByText("'Foo' Test error 1")).toBeInTheDocument();
expect(getByText("'An Example Title' Test error 2")).toBeInTheDocument();
});
it('properties with title', async () => {
const errorList = {
errors: [
{
property: '.foo',
stack: 'Test error 1',
message: 'Test error 1',
},
{
property: '.bar',
stack: 'Test error 2',
message: 'Test error 2',
},
],
errorSchema: {},
schema: {
properties: {
foo: {
title: 'Hello',
},
bar: {
title: 'Example Title',
},
},
},
} as Partial<ErrorListProps> as ErrorListProps;
const { getByText } = await renderInTestApp(
<ErrorListTemplate {...errorList} />,
);
expect(getByText("'Hello' Test error 1")).toBeInTheDocument();
expect(getByText("'Example Title' Test error 2")).toBeInTheDocument();
});
});
});
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React from 'react';
import { ErrorListProps } from '@rjsf/utils';
import { ErrorListProps, RJSFValidationError } from '@rjsf/utils';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
@@ -22,6 +22,7 @@ import ListItemText from '@material-ui/core/ListItemText';
import Paper from '@material-ui/core/Paper';
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles';
import ErrorIcon from '@material-ui/icons/Error';
import startCase from 'lodash/startCase';
const useStyles = makeStyles((_theme: Theme) =>
createStyles({
@@ -39,9 +40,28 @@ const useStyles = makeStyles((_theme: Theme) =>
*
* @public
*/
export const ErrorListTemplate = ({ errors }: ErrorListProps) => {
export const ErrorListTemplate = ({ errors, schema }: ErrorListProps) => {
const classes = useStyles();
function formatErrorMessage(error: RJSFValidationError) {
if (error.property && error.message) {
const propertyName = error.property.startsWith('.')
? error.property.substring(1)
: error.property;
if (schema.properties && propertyName in schema.properties) {
const property = schema.properties[propertyName];
if (typeof property === 'object' && 'title' in property) {
return `'${property.title}' ${error.message}`;
}
}
// fall back to property name
return `'${startCase(propertyName)}' ${error.message}`;
}
// fall back if property does not exist
return error.stack;
}
return (
<Paper>
<List dense className={classes.list}>
@@ -52,7 +72,7 @@ export const ErrorListTemplate = ({ errors }: ErrorListProps) => {
</ListItemIcon>
<ListItemText
classes={{ primary: classes.text }}
primary={error.stack}
primary={formatErrorMessage(error)}
/>
</ListItem>
))}