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,
propertyKey: string,
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 (
jsonSchema.required &&
jsonSchema.required.includes(propertyKey) &&
@ -169,7 +179,8 @@ export default function CustomForm({
const checkFieldsWithCustomValidations = (
jsonSchema: any,
formDataToCheck: any,
errors: any
errors: any,
uiSchemaPassedIn?: any
// eslint-disable-next-line sonarjs/cognitive-complexity
) => {
// if the jsonSchema has an items attribute then assume the element itself
@ -177,9 +188,21 @@ export default function CustomForm({
const jsonSchemaToUse =
'items' in jsonSchema ? jsonSchema.items : jsonSchema;
let uiSchemaToUse = uiSchemaPassedIn;
if (!uiSchemaToUse) {
uiSchemaToUse = uiSchema;
}
if ('items' in uiSchemaToUse) {
uiSchemaToUse = uiSchemaToUse.items;
}
if ('properties' in jsonSchemaToUse) {
Object.keys(jsonSchemaToUse.properties).forEach((propertyKey: string) => {
const propertyMetadata = jsonSchemaToUse.properties[propertyKey];
let currentUiSchema: any = null;
if (propertyKey in uiSchemaToUse) {
currentUiSchema = uiSchemaToUse[propertyKey];
}
if ('minimumDate' in propertyMetadata) {
checkMinimumDate(
formDataToCheck,
@ -195,7 +218,8 @@ export default function CustomForm({
formDataToCheck,
propertyKey,
errors,
jsonSchemaToUse
jsonSchemaToUse,
currentUiSchema
);
}
@ -213,7 +237,8 @@ export default function CustomForm({
checkFieldsWithCustomValidations(
propertyMetadata,
item,
errorsToSend
errorsToSend,
currentUiSchema
);
});
}

View File

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

View File

@ -1,11 +1,9 @@
import React from "react";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormLabel from "@mui/material/FormLabel";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import { WidgetProps } from "@rjsf/utils";
import React from 'react';
import { RadioButtonGroup, RadioButton } from '@carbon/react';
import { WidgetProps } from '@rjsf/utils';
import { getCommonAttributes } from '../../helpers';
const RadioWidget = ({
function RadioWidget({
id,
schema,
options,
@ -17,11 +15,19 @@ const RadioWidget = ({
onChange,
onBlur,
onFocus,
}: WidgetProps) => {
uiSchema,
rawErrors,
}: WidgetProps) {
const { enumOptions, enumDisabled } = options;
const _onChange = (_: any, value: any) =>
onChange(schema.type == "boolean" ? value !== "false" : value);
const _onChange = (newValue: any, _radioButtonId: any) => {
if (schema.type === 'boolean') {
const v: any = newValue === 'true' || newValue === true;
onChange(v);
} else {
onChange(newValue);
}
};
const _onBlur = ({ target: { value } }: React.FocusEvent<HTMLInputElement>) =>
onBlur(id, value);
const _onFocus = ({
@ -30,43 +36,39 @@ const RadioWidget = ({
const row = options ? options.inline : false;
return (
<>
<RadioGroup
id={id}
name={id}
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>
</>
const commonAttributes = getCommonAttributes(
label,
schema,
uiSchema,
rawErrors
);
};
// 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;

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 errorMessageForField = null;
if (rawErrors && rawErrors.length > 0) {
invalid = true;
[errorMessageForFieldWithoutLabel] = rawErrors;
if ('validationErrorMessage' in schema) {
errorMessageForField = (schema as any).validationErrorMessage;
errorMessageForFieldWithoutLabel = errorMessageForField;
} else {
errorMessageForField = `${labelToUse.replace(/\*$/, '')} ${rawErrors[0]}`;
}
@ -41,6 +46,7 @@ export const getCommonAttributes = (
label: labelToUse,
invalid,
errorMessageForField,
errorMessageForFieldWithoutLabel,
labelWithRequiredIndicator,
tooltipText,
};