character-counter-fixes (#1432)
* carbon supports the counter field natively so use that w/ burnettk * added docs for character counter fields w/ burnettk --------- Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
parent
a59f2777cd
commit
a7a9927a5f
|
@ -199,10 +199,10 @@ Utilizing maximum date validation, you can prevent dates from exceeding a certai
|
||||||
|
|
||||||
By incorporating these validations into SpiffWorkflow forms, you can create interactive forms that automatically enforce business rules, improving data quality and user experience.
|
By incorporating these validations into SpiffWorkflow forms, you can create interactive forms that automatically enforce business rules, improving data quality and user experience.
|
||||||
|
|
||||||
|
|
||||||
#### Date Validation Scenario: Enforcing Minimum and Maximum Date Constraints
|
#### Date Validation Scenario: Enforcing Minimum and Maximum Date Constraints
|
||||||
|
|
||||||
#### Scenario Overview
|
#### Scenario Overview
|
||||||
|
|
||||||
Workflow processes often require the enforcement of minimum and maximum date constraints to align with operational timelines or project deadlines. This scenario demonstrates the configuration of both `minimumDate` and `maximumDate` validations within a form, ensuring that selected dates fall within a specific period defined by other date fields in the workflow.
|
Workflow processes often require the enforcement of minimum and maximum date constraints to align with operational timelines or project deadlines. This scenario demonstrates the configuration of both `minimumDate` and `maximumDate` validations within a form, ensuring that selected dates fall within a specific period defined by other date fields in the workflow.
|
||||||
|
|
||||||
#### JSON Schema Configuration:
|
#### JSON Schema Configuration:
|
||||||
|
@ -242,9 +242,10 @@ The "test-maximum-date-schema.json" process model outlines a form structure that
|
||||||
- **Preferred Delivery Date**: A single date indicating when the delivery of a service or product is preferred, bounded by today's date and the `end_date`.
|
- **Preferred Delivery Date**: A single date indicating when the delivery of a service or product is preferred, bounded by today's date and the `end_date`.
|
||||||
- **Preferred Delivery Date Range**: A span of dates indicating an acceptable window for delivery, constrained by today's date and the `end_date`.
|
- **Preferred Delivery Date Range**: A span of dates indicating an acceptable window for delivery, constrained by today's date and the `end_date`.
|
||||||
|
|
||||||
### Implementation in SpiffWorkflow Forms:
|
#### Implementation in SpiffWorkflow Forms:
|
||||||
|
|
||||||
The schema enforces the following rules:
|
The schema enforces the following rules:
|
||||||
|
|
||||||
- The `Preferred Delivery Date` cannot be earlier than today (the `minimumDate`) and not later than the `end_date` (the `maximumDate`).
|
- The `Preferred Delivery Date` cannot be earlier than today (the `minimumDate`) and not later than the `end_date` (the `maximumDate`).
|
||||||
- The `Preferred Delivery Date Range` must start no earlier than today and end no later than the `end_date`.
|
- The `Preferred Delivery Date Range` must start no earlier than today and end no later than the `end_date`.
|
||||||
|
|
||||||
|
@ -263,9 +264,9 @@ Define your form fields in the JSON schema as follows:
|
||||||
"description": "Demonstrating side-by-side layout",
|
"description": "Demonstrating side-by-side layout",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"firstName": {"type": "string"},
|
"firstName": { "type": "string" },
|
||||||
"lastName": {"type": "string"},
|
"lastName": { "type": "string" },
|
||||||
"notes": {"type": "string"}
|
"notes": { "type": "string" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -279,13 +280,14 @@ Here's how to use it:
|
||||||
{
|
{
|
||||||
"ui:layout": [
|
"ui:layout": [
|
||||||
{
|
{
|
||||||
"firstName": {"sm": 2, "md": 2, "lg": 4},
|
"firstName": { "sm": 2, "md": 2, "lg": 4 },
|
||||||
"lastName": {"sm": 2, "md": 2, "lg": 4}
|
"lastName": { "sm": 2, "md": 2, "lg": 4 }
|
||||||
},
|
},
|
||||||
{"notes": {}}
|
{ "notes": {} }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
![Styling_Form](images/styling_forms.png)
|
![Styling_Form](images/styling_forms.png)
|
||||||
|
|
||||||
#### Key Points:
|
#### Key Points:
|
||||||
|
@ -311,11 +313,8 @@ If you just specify a uiSchema like this, it will figure out the column widths f
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
By leveraging the `ui:layout` feature, you can design form layouts that are not only functional but also enhance the user experience, making your forms well-organized and accessible across various screen sizes.
|
By leveraging the `ui:layout` feature, you can design form layouts that are not only functional but also enhance the user experience, making your forms well-organized and accessible across various screen sizes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Display UI Help in Web Forms
|
### Display UI Help in Web Forms
|
||||||
|
|
||||||
When designing web forms, it's essential to provide users with contextual help to ensure they understand the purpose and requirements of each field.
|
When designing web forms, it's essential to provide users with contextual help to ensure they understand the purpose and requirements of each field.
|
||||||
|
@ -393,17 +392,17 @@ Below is an example JSON schema that includes the numeric range field:
|
||||||
"numericRange": {
|
"numericRange": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "Numeric Range",
|
"title": "Numeric Range",
|
||||||
"minimum": {
|
"minimum": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"title": "Minimum Value"
|
"title": "Minimum Value"
|
||||||
},
|
},
|
||||||
"maximum": {
|
"maximum": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"title": "Maximum Value"
|
"title": "Maximum Value"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This schema defines a numeric range object with `min` and `max` properties, both of which are required.
|
This schema defines a numeric range object with `min` and `max` properties, both of which are required.
|
||||||
|
@ -422,7 +421,6 @@ This schema defines a numeric range object with `min` and `max` properties, both
|
||||||
|
|
||||||
This will automatically validate that the max value cannot be less than the min value.
|
This will automatically validate that the max value cannot be less than the min value.
|
||||||
|
|
||||||
|
|
||||||
### Adding a New Button for Repeating Sections in Forms
|
### Adding a New Button for Repeating Sections in Forms
|
||||||
|
|
||||||
Nested forms or repeating sections are designed to collect an array of objects, where each object represents a set of related information. For instance, in a task management form, you might need to collect multiple tasks, each with its title and completion status.
|
Nested forms or repeating sections are designed to collect an array of objects, where each object represents a set of related information. For instance, in a task management form, you might need to collect multiple tasks, each with its title and completion status.
|
||||||
|
@ -458,8 +456,44 @@ This structure can be represented in the form's schema as follows:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Form Preview**:
|
**Form Preview**:
|
||||||
|
|
||||||
![Nested Forms](images/Nested_form_display.png)
|
![Nested Forms](images/Nested_form_display.png)
|
||||||
|
|
||||||
By usign this feature, you can effectively implement new buttons for nested forms or repeating sections improving the form's usability for collecting multiple related entries from users.
|
By usign this feature, you can effectively implement new buttons for nested forms or repeating sections improving the form's usability for collecting multiple related entries from users.
|
||||||
|
|
||||||
|
### Character counter
|
||||||
|
|
||||||
|
To give the user feedback about how they are doing in terms of staying within the limits imposed by the field, you can display a character counter.
|
||||||
|
|
||||||
|
#### JSON Schema Configuration
|
||||||
|
|
||||||
|
To do this, your json schema must contain a string with a maxLength, like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "String with character counter",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"my_hot_string": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### UI Schema Configuration
|
||||||
|
|
||||||
|
Your UI Schema will need a ui:options specifying counter true, like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"my_hot_string": {
|
||||||
|
"ui:options": {
|
||||||
|
"counter": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -10,7 +10,6 @@ import TypeaheadWidget from '../rjsf/custom_widgets/TypeaheadWidget/TypeaheadWid
|
||||||
import MarkDownFieldWidget from '../rjsf/custom_widgets/MarkDownFieldWidget/MarkDownFieldWidget';
|
import MarkDownFieldWidget from '../rjsf/custom_widgets/MarkDownFieldWidget/MarkDownFieldWidget';
|
||||||
import NumericRangeField from '../rjsf/custom_widgets/NumericRangeField/NumericRangeField';
|
import NumericRangeField from '../rjsf/custom_widgets/NumericRangeField/NumericRangeField';
|
||||||
import ObjectFieldRestrictedGridTemplate from '../rjsf/custom_templates/ObjectFieldRestrictGridTemplate';
|
import ObjectFieldRestrictedGridTemplate from '../rjsf/custom_templates/ObjectFieldRestrictGridTemplate';
|
||||||
import CharacterCounterField from '../rjsf/custom_widgets/CharacterCounterField/CharacterCounterField';
|
|
||||||
|
|
||||||
enum DateCheckType {
|
enum DateCheckType {
|
||||||
minimum = 'minimum',
|
minimum = 'minimum',
|
||||||
|
@ -57,7 +56,6 @@ export default function CustomForm({
|
||||||
// set in uiSchema using the "ui:field" key for a property
|
// set in uiSchema using the "ui:field" key for a property
|
||||||
const rjsfFields: RegistryFieldsType = {
|
const rjsfFields: RegistryFieldsType = {
|
||||||
'numeric-range': NumericRangeField,
|
'numeric-range': NumericRangeField,
|
||||||
'character-counter': CharacterCounterField,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const rjsfTemplates: any = {};
|
const rjsfTemplates: any = {};
|
||||||
|
|
|
@ -89,6 +89,19 @@ export default function BaseInputTemplate<
|
||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let enableCounter = false;
|
||||||
|
let maxCount = undefined;
|
||||||
|
if (options && options.counter) {
|
||||||
|
enableCounter = true;
|
||||||
|
if (schema && schema.maxLength) {
|
||||||
|
maxCount = schema.maxLength;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Counter was requested but no maxLength given on the ${label}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const commonAttributes = getCommonAttributes(
|
const commonAttributes = getCommonAttributes(
|
||||||
label,
|
label,
|
||||||
schema,
|
schema,
|
||||||
|
@ -157,6 +170,8 @@ export default function BaseInputTemplate<
|
||||||
onChange={_onChange}
|
onChange={_onChange}
|
||||||
onBlur={_onBlur}
|
onBlur={_onBlur}
|
||||||
onFocus={_onFocus}
|
onFocus={_onFocus}
|
||||||
|
enableCounter={enableCounter}
|
||||||
|
maxCount={maxCount}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -59,6 +59,19 @@ function TextareaWidget<
|
||||||
rawErrors
|
rawErrors
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let enableCounter = false;
|
||||||
|
let maxCount = undefined;
|
||||||
|
if (options && options.counter) {
|
||||||
|
enableCounter = true;
|
||||||
|
if (schema && schema.maxLength) {
|
||||||
|
maxCount = schema.maxLength;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Counter was requested but no maxLength given on the ${label}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextArea
|
<TextArea
|
||||||
id={id}
|
id={id}
|
||||||
|
@ -78,6 +91,8 @@ function TextareaWidget<
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
invalid={commonAttributes.invalid}
|
invalid={commonAttributes.invalid}
|
||||||
invalidText={commonAttributes.errorMessageForField}
|
invalidText={commonAttributes.errorMessageForField}
|
||||||
|
enableCounter={enableCounter}
|
||||||
|
maxCount={maxCount}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
import {
|
|
||||||
descriptionId,
|
|
||||||
FieldProps,
|
|
||||||
getTemplate,
|
|
||||||
getUiOptions,
|
|
||||||
} from '@rjsf/utils';
|
|
||||||
import { TextInput } from '@carbon/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { getCommonAttributes } from '../../helpers';
|
|
||||||
|
|
||||||
// Example jsonSchema - NOTE: the "min" and "max" properties are special names and must be used:
|
|
||||||
// "name":{
|
|
||||||
// "title": "Name",
|
|
||||||
// "type": "string",
|
|
||||||
// "maxLength": 999999999999,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Example uiSchema:
|
|
||||||
// "name": {
|
|
||||||
// "ui:field": "character-counter",
|
|
||||||
// }
|
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
||||||
export default function CharacterCounterField({
|
|
||||||
id,
|
|
||||||
schema,
|
|
||||||
uiSchema,
|
|
||||||
idSchema,
|
|
||||||
disabled,
|
|
||||||
readonly,
|
|
||||||
onChange,
|
|
||||||
autofocus,
|
|
||||||
label,
|
|
||||||
rawErrors = [],
|
|
||||||
formData,
|
|
||||||
registry,
|
|
||||||
required,
|
|
||||||
}: FieldProps) {
|
|
||||||
const commonAttributes = getCommonAttributes(
|
|
||||||
label,
|
|
||||||
schema,
|
|
||||||
uiSchema,
|
|
||||||
rawErrors
|
|
||||||
);
|
|
||||||
|
|
||||||
const description = schema?.description || uiSchema?.['ui:description'];
|
|
||||||
|
|
||||||
const uiOptions = getUiOptions(uiSchema || {});
|
|
||||||
const DescriptionFieldTemplate = getTemplate(
|
|
||||||
'DescriptionFieldTemplate',
|
|
||||||
registry,
|
|
||||||
uiOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
if (schema.maxLength === undefined) {
|
|
||||||
throw new Error(
|
|
||||||
'CharacterCounterTextField requires a "maxLength" property to be specified in the schema'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const text = formData || '';
|
|
||||||
|
|
||||||
const onChangeLocal = (event: any) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (!disabled && !readonly) {
|
|
||||||
onChange(event.target.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="character---counter--text-field-wrapper">
|
|
||||||
<div className="character---counter--text-field-label">
|
|
||||||
<h5>
|
|
||||||
{required ? `${commonAttributes.label} *` : commonAttributes.label}
|
|
||||||
</h5>
|
|
||||||
{description && (
|
|
||||||
<div className="markdown-field-desc-text">
|
|
||||||
<DescriptionFieldTemplate
|
|
||||||
id={descriptionId(idSchema)}
|
|
||||||
description={description}
|
|
||||||
schema={schema}
|
|
||||||
uiSchema={uiSchema}
|
|
||||||
registry={registry}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<TextInput
|
|
||||||
id={id}
|
|
||||||
type="text"
|
|
||||||
disabled={disabled}
|
|
||||||
readonly={readonly}
|
|
||||||
value={text}
|
|
||||||
onChange={(event: any) => {
|
|
||||||
onChangeLocal(event);
|
|
||||||
}}
|
|
||||||
invalid={commonAttributes.invalid}
|
|
||||||
enableCounter
|
|
||||||
maxCount={schema.maxLength}
|
|
||||||
autoFocus={autofocus}
|
|
||||||
/>
|
|
||||||
{commonAttributes.errorMessageForField && (
|
|
||||||
<div className="error-message">
|
|
||||||
{commonAttributes.errorMessageForField}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{commonAttributes.helperText && (
|
|
||||||
<p className="character---counter--text-field-help-text">
|
|
||||||
{commonAttributes.helperText}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in New Issue