feature/fix-required-radio-buttons (#525)

* ignore validations for radio booleans from the custom form w/ burnettk

* use carbon for radio buttons

* added comment about error message without label

---------

Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
jasquat 2023-10-04 10:57:15 -04:00 committed by GitHub
parent 34a0323c4d
commit acc918b664
4 changed files with 85 additions and 52 deletions

View File

@ -152,8 +152,18 @@ export default function CustomForm({
formDataToCheck: any, formDataToCheck: any,
propertyKey: string, propertyKey: string,
errors: any, errors: any,
jsonSchema: any jsonSchema: any,
uiSchemaPassedIn?: any
) => { ) => {
// this validation only applies to checkboxes,
// other forms of booleans are validated differently
if (
uiSchemaPassedIn &&
'ui:widget' in uiSchemaPassedIn &&
uiSchemaPassedIn['ui:widget'] !== 'checkbox'
) {
return;
}
if ( if (
jsonSchema.required && jsonSchema.required &&
jsonSchema.required.includes(propertyKey) && jsonSchema.required.includes(propertyKey) &&
@ -169,7 +179,8 @@ export default function CustomForm({
const checkFieldsWithCustomValidations = ( const checkFieldsWithCustomValidations = (
jsonSchema: any, jsonSchema: any,
formDataToCheck: any, formDataToCheck: any,
errors: any errors: any,
uiSchemaPassedIn?: any
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
) => { ) => {
// if the jsonSchema has an items attribute then assume the element itself // if the jsonSchema has an items attribute then assume the element itself
@ -177,9 +188,21 @@ export default function CustomForm({
const jsonSchemaToUse = const jsonSchemaToUse =
'items' in jsonSchema ? jsonSchema.items : jsonSchema; 'items' in jsonSchema ? jsonSchema.items : jsonSchema;
let uiSchemaToUse = uiSchemaPassedIn;
if (!uiSchemaToUse) {
uiSchemaToUse = uiSchema;
}
if ('items' in uiSchemaToUse) {
uiSchemaToUse = uiSchemaToUse.items;
}
if ('properties' in jsonSchemaToUse) { if ('properties' in jsonSchemaToUse) {
Object.keys(jsonSchemaToUse.properties).forEach((propertyKey: string) => { Object.keys(jsonSchemaToUse.properties).forEach((propertyKey: string) => {
const propertyMetadata = jsonSchemaToUse.properties[propertyKey]; const propertyMetadata = jsonSchemaToUse.properties[propertyKey];
let currentUiSchema: any = null;
if (propertyKey in uiSchemaToUse) {
currentUiSchema = uiSchemaToUse[propertyKey];
}
if ('minimumDate' in propertyMetadata) { if ('minimumDate' in propertyMetadata) {
checkMinimumDate( checkMinimumDate(
formDataToCheck, formDataToCheck,
@ -195,7 +218,8 @@ export default function CustomForm({
formDataToCheck, formDataToCheck,
propertyKey, propertyKey,
errors, errors,
jsonSchemaToUse jsonSchemaToUse,
currentUiSchema
); );
} }
@ -213,7 +237,8 @@ export default function CustomForm({
checkFieldsWithCustomValidations( checkFieldsWithCustomValidations(
propertyMetadata, propertyMetadata,
item, item,
errorsToSend errorsToSend,
currentUiSchema
); );
}); });
} }

View File

@ -57,7 +57,7 @@ function CheckboxWidget(props: WidgetProps) {
title={commonAttributes.tooltipText} title={commonAttributes.tooltipText}
autoFocus={autofocus} autoFocus={autofocus}
invalid={commonAttributes.invalid} invalid={commonAttributes.invalid}
invalidText={commonAttributes.errorMessageForField} invalidText={commonAttributes.errorMessageForFieldWithoutLabel}
helperText={commonAttributes.helperText} helperText={commonAttributes.helperText}
labelText={ labelText={
required required

View File

@ -1,11 +1,9 @@
import React from "react"; import React from 'react';
import FormControlLabel from "@mui/material/FormControlLabel"; import { RadioButtonGroup, RadioButton } from '@carbon/react';
import FormLabel from "@mui/material/FormLabel"; import { WidgetProps } from '@rjsf/utils';
import Radio from "@mui/material/Radio"; import { getCommonAttributes } from '../../helpers';
import RadioGroup from "@mui/material/RadioGroup";
import { WidgetProps } from "@rjsf/utils";
const RadioWidget = ({ function RadioWidget({
id, id,
schema, schema,
options, options,
@ -17,11 +15,19 @@ const RadioWidget = ({
onChange, onChange,
onBlur, onBlur,
onFocus, onFocus,
}: WidgetProps) => { uiSchema,
rawErrors,
}: WidgetProps) {
const { enumOptions, enumDisabled } = options; const { enumOptions, enumDisabled } = options;
const _onChange = (_: any, value: any) => const _onChange = (newValue: any, _radioButtonId: any) => {
onChange(schema.type == "boolean" ? value !== "false" : value); if (schema.type === 'boolean') {
const v: any = newValue === 'true' || newValue === true;
onChange(v);
} else {
onChange(newValue);
}
};
const _onBlur = ({ target: { value } }: React.FocusEvent<HTMLInputElement>) => const _onBlur = ({ target: { value } }: React.FocusEvent<HTMLInputElement>) =>
onBlur(id, value); onBlur(id, value);
const _onFocus = ({ const _onFocus = ({
@ -30,43 +36,39 @@ const RadioWidget = ({
const row = options ? options.inline : false; const row = options ? options.inline : false;
return ( const commonAttributes = getCommonAttributes(
<> label,
<RadioGroup schema,
id={id} uiSchema,
name={id} rawErrors
value={`${value}`}
row={row as boolean}
onChange={_onChange}
onBlur={_onBlur}
onFocus={_onFocus}
>
{Array.isArray(enumOptions) &&
enumOptions.map((option) => {
const itemDisabled =
Array.isArray(enumDisabled) &&
enumDisabled.indexOf(option.value) !== -1;
const radio = (
<FormControlLabel
control={
<Radio
name={id}
id={`${id}-${option.value}`}
color="primary"
/>
}
label={`${option.label}`}
value={`${option.value}`}
key={option.value}
disabled={disabled || itemDisabled || readonly}
/>
);
return radio;
})}
</RadioGroup>
</>
); );
};
// pass values in as strings so we can support both boolean and string radio buttons
return (
<RadioButtonGroup
id={id}
name={id}
legendText={commonAttributes.helperText}
valueSelected={`${value}`}
invalid={commonAttributes.invalid}
invalidText={commonAttributes.errorMessageForFieldWithoutLabel}
disabled={disabled || readonly}
onChange={_onChange}
onBlur={_onBlur}
onFocus={_onFocus}
>
{Array.isArray(enumOptions) &&
enumOptions.map((option) => {
return (
<RadioButton
id={`${id}-${option.value}`}
labelText={option.label}
value={`${option.value}`}
/>
);
})}
</RadioButtonGroup>
);
}
export default RadioWidget; export default RadioWidget;

View File

@ -25,12 +25,17 @@ export const getCommonAttributes = (
} }
} }
// some rjsf validations add in labels by default so avoid showing it twice
let errorMessageForFieldWithoutLabel = null;
let invalid = false; let invalid = false;
let errorMessageForField = null; let errorMessageForField = null;
if (rawErrors && rawErrors.length > 0) { if (rawErrors && rawErrors.length > 0) {
invalid = true; invalid = true;
[errorMessageForFieldWithoutLabel] = rawErrors;
if ('validationErrorMessage' in schema) { if ('validationErrorMessage' in schema) {
errorMessageForField = (schema as any).validationErrorMessage; errorMessageForField = (schema as any).validationErrorMessage;
errorMessageForFieldWithoutLabel = errorMessageForField;
} else { } else {
errorMessageForField = `${labelToUse.replace(/\*$/, '')} ${rawErrors[0]}`; errorMessageForField = `${labelToUse.replace(/\*$/, '')} ${rawErrors[0]}`;
} }
@ -41,6 +46,7 @@ export const getCommonAttributes = (
label: labelToUse, label: labelToUse,
invalid, invalid,
errorMessageForField, errorMessageForField,
errorMessageForFieldWithoutLabel,
labelWithRequiredIndicator, labelWithRequiredIndicator,
tooltipText, tooltipText,
}; };