diff --git a/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx b/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx
index 3daaf14d..8954aba1 100644
--- a/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx
+++ b/spiffworkflow-frontend/src/rjsf/carbon_theme/BaseInputTemplate/BaseInputTemplate.tsx
@@ -1,4 +1,3 @@
-// @ts-ignore
import { DatePicker, DatePickerInput, TextInput } from '@carbon/react';
import {
getInputProps,
@@ -11,6 +10,7 @@ import {
import { useCallback } from 'react';
import { DATE_FORMAT_CARBON, DATE_FORMAT_FOR_DISPLAY } from '../../../config';
import { ymdDateStringToConfiguredFormat } from '../../../helpers';
+import { getCommonAttributes } from '../../helpers';
/** The `BaseInputTemplate` is the template to use to render the basic `` component for the `core` theme.
* It is used as the template for rendering many of the based widgets that differ by `type` and callbacks only.
@@ -70,28 +70,12 @@ export default function BaseInputTemplate<
[onFocus, id]
);
- let labelToUse = label;
- if (uiSchema && uiSchema['ui:title']) {
- labelToUse = uiSchema['ui:title'];
- } else if (schema && schema.title) {
- labelToUse = schema.title;
- }
-
- let helperText = null;
- if (uiSchema && uiSchema['ui:help']) {
- helperText = uiSchema['ui:help'];
- }
-
- let invalid = false;
- let errorMessageForField = null;
- if (rawErrors && rawErrors.length > 0) {
- invalid = true;
- if ('validationErrorMessage' in schema) {
- errorMessageForField = (schema as any).validationErrorMessage;
- } else {
- errorMessageForField = `${labelToUse.replace(/\*$/, '')} ${rawErrors[0]}`;
- }
- }
+ const commonAttributes = getCommonAttributes(
+ label,
+ schema,
+ uiSchema,
+ rawErrors
+ );
let component = null;
if (type === 'date') {
@@ -118,15 +102,15 @@ export default function BaseInputTemplate<
onChange(checked);
+ const _onChange = (_: any, newValue: any) => {
+ // if this field is required and it is not checked then change the value to undefined
+ // otherwise rjsf will not flag this field as invalid
+ if (required && !newValue.checked) {
+ onChange(undefined);
+ } else {
+ onChange(newValue.checked);
+ }
+ };
const _onBlur = ({
target: { value },
}: React.FocusEvent) => onBlur(id, value);
@@ -29,22 +35,32 @@ function CheckboxWidget(props: WidgetProps) {
target: { value },
}: React.FocusEvent) => onFocus(id, value);
+ const commonAttributes = getCommonAttributes(
+ label,
+ schema,
+ uiSchema,
+ rawErrors
+ );
+
return (
-
+
);
}
diff --git a/spiffworkflow-frontend/src/rjsf/carbon_theme/SelectWidget/SelectWidget.tsx b/spiffworkflow-frontend/src/rjsf/carbon_theme/SelectWidget/SelectWidget.tsx
index f61d509c..525f7859 100644
--- a/spiffworkflow-frontend/src/rjsf/carbon_theme/SelectWidget/SelectWidget.tsx
+++ b/spiffworkflow-frontend/src/rjsf/carbon_theme/SelectWidget/SelectWidget.tsx
@@ -1,5 +1,6 @@
import { Select, SelectItem } from '@carbon/react';
import { WidgetProps, processSelectValue } from '@rjsf/utils';
+import { getCommonAttributes } from '../../helpers';
function SelectWidget({
schema,
@@ -35,30 +36,12 @@ function SelectWidget({
}: React.FocusEvent) =>
onFocus(id, processSelectValue(schema, value, options));
- let labelToUse = label;
- if (uiSchema && uiSchema['ui:title']) {
- labelToUse = uiSchema['ui:title'];
- } else if (schema && schema.title) {
- labelToUse = schema.title;
- }
- let helperText = null;
- if (uiSchema && uiSchema['ui:help']) {
- helperText = uiSchema['ui:help'];
- }
- if (required) {
- labelToUse = `${labelToUse}*`;
- }
-
- let invalid = false;
- let errorMessageForField = null;
- if (rawErrors && rawErrors.length > 0) {
- invalid = true;
- if ('validationErrorMessage' in schema) {
- errorMessageForField = (schema as any).validationErrorMessage;
- } else {
- errorMessageForField = rawErrors[0];
- }
- }
+ const commonAttributes = getCommonAttributes(
+ label,
+ schema,
+ uiSchema,
+ rawErrors
+ );
// ok. so in safari, the select widget showed the first option, whereas in chrome it forced you to select an option.
// this change causes causes safari to act a little bit more like chrome, but it's different because we are actually adding
@@ -99,7 +82,7 @@ function SelectWidget({
name={id}
labelText=""
select
- helperText={helperText}
+ helperText={commonAttributes.helperText}
value={typeof value === 'undefined' ? emptyValue : value}
disabled={disabled || readonly}
autoFocus={autofocus}
@@ -107,8 +90,8 @@ function SelectWidget({
onChange={_onChange}
onBlur={_onBlur}
onFocus={_onFocus}
- invalid={invalid}
- invalidText={errorMessageForField}
+ invalid={commonAttributes.invalid}
+ invalidText={commonAttributes.errorMessageForField}
InputLabelProps={{
shrink: true,
}}
@@ -116,7 +99,7 @@ function SelectWidget({
multiple: typeof multiple === 'undefined' ? false : multiple,
}}
>
- {(enumOptions as any).map(({ value, label }: any, i: number) => {
+ {(enumOptions as any).map(({ value, label }: any, _i: number) => {
const disabled: any =
enumDisabled && (enumDisabled as any).indexOf(value) != -1;
return ;
diff --git a/spiffworkflow-frontend/src/rjsf/carbon_theme/TextareaWidget/TextareaWidget.tsx b/spiffworkflow-frontend/src/rjsf/carbon_theme/TextareaWidget/TextareaWidget.tsx
index adec640c..9810aeb0 100644
--- a/spiffworkflow-frontend/src/rjsf/carbon_theme/TextareaWidget/TextareaWidget.tsx
+++ b/spiffworkflow-frontend/src/rjsf/carbon_theme/TextareaWidget/TextareaWidget.tsx
@@ -7,6 +7,7 @@ import {
StrictRJSFSchema,
WidgetProps,
} from '@rjsf/utils';
+import { getCommonAttributes } from '../../helpers';
/** The `TextareaWidget` is a widget for rendering input fields as textarea.
*
@@ -51,34 +52,19 @@ function TextareaWidget<
[id, onFocus]
);
- let labelToUse = label;
- if (uiSchema && uiSchema['ui:title']) {
- labelToUse = uiSchema['ui:title'];
- } else if (schema && schema.title) {
- labelToUse = schema.title;
- }
- if (required) {
- labelToUse = `${labelToUse}*`;
- }
-
- let helperText = null;
- if (uiSchema && uiSchema['ui:help']) {
- helperText = uiSchema['ui:help'];
- }
-
- let invalid = false;
- let errorMessageForField = null;
- if (rawErrors && rawErrors.length > 0) {
- invalid = true;
- errorMessageForField = rawErrors[0];
- }
+ const commonAttributes = getCommonAttributes(
+ label,
+ schema,
+ uiSchema,
+ rawErrors
+ );
return (
);
}
diff --git a/spiffworkflow-frontend/src/rjsf/custom_widgets/DateRangePicker/DateRangePickerWidget.tsx b/spiffworkflow-frontend/src/rjsf/custom_widgets/DateRangePicker/DateRangePickerWidget.tsx
index d6be4c36..2aeefc7f 100644
--- a/spiffworkflow-frontend/src/rjsf/custom_widgets/DateRangePicker/DateRangePickerWidget.tsx
+++ b/spiffworkflow-frontend/src/rjsf/custom_widgets/DateRangePicker/DateRangePickerWidget.tsx
@@ -10,10 +10,12 @@ import {
convertStringToDate,
dateStringToYMDFormat,
} from '../../../helpers';
+import { getCommonAttributes } from '../../helpers';
interface widgetArgs {
id: string;
value: any;
+ label: string;
schema?: any;
uiSchema?: any;
disabled?: boolean;
@@ -21,7 +23,6 @@ interface widgetArgs {
rawErrors?: any;
onChange?: any;
autofocus?: any;
- label?: string;
}
// NOTE: To properly validate that both start and end dates are specified
@@ -41,15 +42,12 @@ export default function DateRangePickerWidget({
label,
rawErrors = [],
}: widgetArgs) {
- let invalid = false;
- let errorMessageForField = null;
-
- let labelToUse = label;
- if (uiSchema && uiSchema['ui:title']) {
- labelToUse = uiSchema['ui:title'];
- } else if (schema && schema.title) {
- labelToUse = schema.title;
- }
+ const commonAttributes = getCommonAttributes(
+ label,
+ schema,
+ uiSchema,
+ rawErrors
+ );
const onChangeLocal = useCallback(
(dateRange: Date[]) => {
@@ -69,22 +67,6 @@ export default function DateRangePickerWidget({
[onChange]
);
- let helperText = null;
- if (uiSchema && uiSchema['ui:help']) {
- helperText = uiSchema['ui:help'];
- }
-
- if (!invalid && rawErrors && rawErrors.length > 0) {
- invalid = true;
- if ('validationErrorMessage' in schema) {
- errorMessageForField = (schema as any).validationErrorMessage;
- } else {
- errorMessageForField = `${(labelToUse || '').replace(/\*$/, '')} ${
- rawErrors[0]
- }`;
- }
- }
-
let dateValue: (Date | null)[] | null = value;
if (value) {
const [startDateString, endDateString] = value.split(DATE_RANGE_DELIMITER);
@@ -113,12 +95,12 @@ export default function DateRangePickerWidget({
@@ -128,7 +110,7 @@ export default function DateRangePickerWidget({
type="text"
size="md"
disabled={disabled || readonly}
- invalid={invalid}
+ invalid={commonAttributes.invalid}
autoFocus={autofocus}
pattern={null}
/>
diff --git a/spiffworkflow-frontend/src/rjsf/custom_widgets/TypeaheadWidget/TypeaheadWidget.tsx b/spiffworkflow-frontend/src/rjsf/custom_widgets/TypeaheadWidget/TypeaheadWidget.tsx
index 4667fdfd..4b662779 100644
--- a/spiffworkflow-frontend/src/rjsf/custom_widgets/TypeaheadWidget/TypeaheadWidget.tsx
+++ b/spiffworkflow-frontend/src/rjsf/custom_widgets/TypeaheadWidget/TypeaheadWidget.tsx
@@ -1,19 +1,20 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { ComboBox } from '@carbon/react';
import HttpService from '../../../services/HttpService';
+import { getCommonAttributes } from '../../helpers';
interface typeaheadArgs {
id: string;
onChange: any;
options: any;
value: any;
+ label: string;
schema?: any;
uiSchema?: any;
disabled?: boolean;
readonly?: boolean;
rawErrors?: any;
placeholder?: string;
- label?: string;
}
// eslint-disable-next-line sonarjs/cognitive-complexity
@@ -36,30 +37,23 @@ export default function TypeaheadWidget({
const itemFormatRegex = /[^{}]+(?=})/g;
let itemFormatSubstitutions: string[] | null = null;
- let invalid = false;
- let errorMessageForField = null;
+
+ const commonAttributes = getCommonAttributes(
+ label,
+ schema,
+ uiSchema,
+ rawErrors
+ );
if (itemFormat) {
try {
itemFormatSubstitutions = itemFormat.match(itemFormatRegex);
} catch (e) {
- errorMessageForField = `itemFormat does not contain replacement keys in curly braces. It should be like: "{key1} ({key2} - {key3})"`;
- invalid = true;
+ commonAttributes.errorMessageForField = `itemFormat does not contain replacement keys in curly braces. It should be like: "{key1} ({key2} - {key3})"`;
+ commonAttributes.invalid = true;
}
}
- let labelToUse = label;
- if (uiSchema && uiSchema['ui:title']) {
- labelToUse = uiSchema['ui:title'];
- } else if (schema && schema.title) {
- labelToUse = schema.title;
- }
-
- if (!category) {
- errorMessageForField = `category is not set in the ui:options for this field: ${labelToUse}`;
- invalid = true;
- }
-
const typeaheadSearch = useCallback(
(inputText: string) => {
const pathForCategory = (text: string) => {
@@ -109,20 +103,9 @@ export default function TypeaheadWidget({
placeholderText = placeholder;
}
- let helperText = null;
- if (uiSchema && uiSchema['ui:help']) {
- helperText = uiSchema['ui:help'];
- }
-
- if (!invalid && rawErrors && rawErrors.length > 0) {
- invalid = true;
- if ('validationErrorMessage' in schema) {
- errorMessageForField = (schema as any).validationErrorMessage;
- } else {
- errorMessageForField = `${(labelToUse || '').replace(/\*$/, '')} ${
- rawErrors[0]
- }`;
- }
+ if (!category) {
+ commonAttributes.errorMessageForField = `category is not set in the ui:options for this field: ${commonAttributes.label}`;
+ commonAttributes.invalid = true;
}
return (
@@ -145,11 +128,11 @@ export default function TypeaheadWidget({
itemToString={itemToString}
placeholder={placeholderText}
selectedItem={selectedItem}
- helperText={helperText}
+ helperText={commonAttributes.helperText}
disabled={disabled}
readOnly={readonly}
- invalid={invalid}
- invalidText={errorMessageForField}
+ invalid={commonAttributes.invalid}
+ invalidText={commonAttributes.errorMessageForField}
/>
);
}
diff --git a/spiffworkflow-frontend/src/rjsf/helpers.tsx b/spiffworkflow-frontend/src/rjsf/helpers.tsx
new file mode 100644
index 00000000..779f3a9b
--- /dev/null
+++ b/spiffworkflow-frontend/src/rjsf/helpers.tsx
@@ -0,0 +1,47 @@
+const REQUIRED_FIELD_SYMBOL = '*';
+export const getCommonAttributes = (
+ label: string,
+ schema: any,
+ uiSchema: any,
+ rawErrors: any
+) => {
+ let labelToUse = label;
+ if (uiSchema && uiSchema['ui:title']) {
+ labelToUse = uiSchema['ui:title'];
+ } else if (schema && schema.title) {
+ labelToUse = schema.title;
+ }
+
+ const labelWithRequiredIndicator = `${labelToUse}${REQUIRED_FIELD_SYMBOL}`;
+
+ let helperText = null;
+ let tooltipText = null;
+ if (uiSchema) {
+ if (uiSchema['ui:help']) {
+ helperText = uiSchema['ui:help'];
+ }
+ if (uiSchema['ui:tooltip']) {
+ tooltipText = uiSchema['ui:tooltip'];
+ }
+ }
+
+ let invalid = false;
+ let errorMessageForField = null;
+ if (rawErrors && rawErrors.length > 0) {
+ invalid = true;
+ if ('validationErrorMessage' in schema) {
+ errorMessageForField = (schema as any).validationErrorMessage;
+ } else {
+ errorMessageForField = `${labelToUse.replace(/\*$/, '')} ${rawErrors[0]}`;
+ }
+ }
+
+ return {
+ helperText,
+ label: labelToUse,
+ invalid,
+ errorMessageForField,
+ labelWithRequiredIndicator,
+ tooltipText,
+ };
+};