fixed error messaging a little bit for forms w/ burnettk cullerton

This commit is contained in:
jasquat 2022-11-16 17:12:01 -05:00
parent 498292d354
commit 17e6605306
3 changed files with 97 additions and 118 deletions

View File

@ -1,57 +1,80 @@
import React from 'react';
// @ts-ignore // @ts-ignore
import { TextInput } from '@carbon/react'; import { TextInput } from '@carbon/react';
import { getInputProps, WidgetProps } from '@rjsf/utils'; import {
getInputProps,
FormContextType,
RJSFSchema,
StrictRJSFSchema,
WidgetProps,
} from '@rjsf/utils';
function BaseInputTemplate({ import { useCallback } from 'react';
id,
placeholder, /** The `BaseInputTemplate` is the template to use to render the basic `<input>` component for the `core` theme.
required, * It is used as the template for rendering many of the <input> based widgets that differ by `type` and callbacks only.
readonly, * It can be customized/overridden for other themes or individual implementations as needed.
disabled, *
type, * @param props - The `WidgetProps` for this template
label, */
value, export default function BaseInputTemplate<
onChange, T = any,
onBlur, S extends StrictRJSFSchema = RJSFSchema,
onFocus, F extends FormContextType = any
autofocus, >(props: WidgetProps<T, S, F>) {
options, const {
schema, id,
uiSchema, value,
rawErrors = [], readonly,
registry, disabled,
...textFieldProps autofocus,
}: WidgetProps) { label,
const inputProps = getInputProps(schema, type, options); onBlur,
// Now we need to pull out the step, min, max into an inner `inputProps` for material-ui onFocus,
const { step, min, max, ...rest } = inputProps; onChange,
const otherProps = { required,
inputProps: { options,
step, schema,
min, uiSchema,
max, formContext,
...(schema.examples ? { list: `examples_${id}` } : undefined), registry,
}, rawErrors,
type,
...rest
} = props;
// Note: since React 15.2.0 we can't forward unknown element attributes, so we
// exclude the "options" and "schema" ones here.
if (!id) {
console.log('No id for', props);
throw new Error(`no id for props ${JSON.stringify(props)}`);
}
const inputProps = {
...rest, ...rest,
...getInputProps<T, S, F>(schema, type, options),
}; };
const localOnChange = ({
// eslint-disable-next-line no-shadow
target: { value },
}: React.ChangeEvent<HTMLInputElement>) => {
onChange(value === '' ? options.emptyValue : value);
};
const localOnBlur = ({
// eslint-disable-next-line no-shadow
target: { value },
}: React.FocusEvent<HTMLInputElement>) => onBlur(id, value);
const localOnFocus = ({
// eslint-disable-next-line no-shadow
target: { value },
}: React.FocusEvent<HTMLInputElement>) => onFocus(id, value);
const { schemaUtils } = registry; let inputValue;
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema); if (inputProps.type === 'number' || inputProps.type === 'integer') {
inputValue = value || value === 0 ? value : '';
} else {
inputValue = value == null ? '' : value;
}
const _onChange = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) =>
onChange(value === '' ? options.emptyValue : value),
[onChange, options]
);
const _onBlur = useCallback(
({ target: { value } }: React.FocusEvent<HTMLInputElement>) =>
onBlur(id, value),
[onBlur, id]
);
const _onFocus = useCallback(
({ target: { value } }: React.FocusEvent<HTMLInputElement>) =>
onFocus(id, value),
[onFocus, id]
);
let labelToUse = label; let labelToUse = label;
if (uiSchema && uiSchema['ui:title']) { if (uiSchema && uiSchema['ui:title']) {
@ -59,39 +82,33 @@ function BaseInputTemplate({
} else if (schema && schema.title) { } else if (schema && schema.title) {
labelToUse = schema.title; labelToUse = schema.title;
} }
if (required) {
labelToUse = `${labelToUse}*`;
}
return ( return (
<> <>
<TextInput <TextInput
id={id} id={id}
name={id} name={id}
placeholder={placeholder} labelText={labelToUse}
labelText={displayLabel ? labelToUse : false}
autoFocus={autofocus} autoFocus={autofocus}
disabled={disabled || readonly} disabled={disabled || readonly}
value={value || value === 0 ? value : ''} value={value || value === 0 ? value : ''}
error={rawErrors.length > 0} onChange={_onChange}
onChange={localOnChange} onBlur={_onBlur}
onBlur={localOnBlur} onFocus={_onFocus}
onFocus={localOnFocus}
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
{...otherProps} {...inputProps}
/> />
{schema.examples && ( {Array.isArray(schema.examples) && (
<datalist id={`examples_${id}`}> <datalist key={`datalist_${id}`} id={`examples_${id}`}>
{(schema.examples as string[]) {[
.concat(schema.default ? ([schema.default] as string[]) : []) ...new Set(
.map((example: any) => { schema.examples.concat(schema.default ? [schema.default] : [])
// eslint-disable-next-line jsx-a11y/control-has-associated-label ),
return <option key={example} value={example} />; ].map((example: any) => (
})} <option key={example} value={example} />
))}
</datalist> </datalist>
)} )}
</> </>
); );
} }
export default BaseInputTemplate;

View File

@ -1,34 +1,16 @@
import React from 'react';
import ErrorIcon from '@mui/icons-material/Error';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import { ErrorListProps } from '@rjsf/utils'; import { ErrorListProps } from '@rjsf/utils';
// @ts-ignore
import { Tag } from '@carbon/react';
function ErrorList({ errors }: ErrorListProps) { function ErrorList({ errors }: ErrorListProps) {
return ( if (errors) {
<Paper elevation={2}> return (
<Box mb={2} p={2}> <Tag type="red" size="md" title="Fill Required Fields">
<Typography variant="h6">Errors</Typography> Please fill out required fields
<List dense> </Tag>
{errors.map((error, i: number) => { );
return ( }
<ListItem key={i}> return null;
<ListItemIcon>
<ErrorIcon color="error" />
</ListItemIcon>
<ListItemText primary={error.stack} />
</ListItem>
);
})}
</List>
</Box>
</Paper>
);
} }
export default ErrorList; export default ErrorList;

View File

@ -1,29 +1,9 @@
import React from 'react';
import { FieldErrorProps } from '@rjsf/utils'; import { FieldErrorProps } from '@rjsf/utils';
import ListItem from '@mui/material/ListItem';
import FormHelperText from '@mui/material/FormHelperText';
import List from '@mui/material/List';
/** The `FieldErrorTemplate` component renders the errors local to the particular field /** The `FieldErrorTemplate` component renders the errors local to the particular field
* *
* @param props - The `FieldErrorProps` for the errors being rendered * @param props - The `FieldErrorProps` for the errors being rendered
*/ */
export default function FieldErrorTemplate(props: FieldErrorProps) { export default function FieldErrorTemplate(_props: FieldErrorProps) {
const { errors = [], idSchema } = props; return null;
if (errors.length === 0) {
return null;
}
const id = `${idSchema.$id}__error`;
return (
<List dense disablePadding>
{errors.map((error, i: number) => {
return (
<ListItem key={i} disableGutters>
<FormHelperText id={id}>{error}</FormHelperText>
</ListItem>
);
})}
</List>
);
} }