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.
|
||||
|
||||
|
||||
#### Date Validation Scenario: Enforcing Minimum and Maximum Date Constraints
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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 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 `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`.
|
||||
|
||||
|
@ -263,9 +264,9 @@ Define your form fields in the JSON schema as follows:
|
|||
"description": "Demonstrating side-by-side layout",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"firstName": {"type": "string"},
|
||||
"lastName": {"type": "string"},
|
||||
"notes": {"type": "string"}
|
||||
"firstName": { "type": "string" },
|
||||
"lastName": { "type": "string" },
|
||||
"notes": { "type": "string" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -279,13 +280,14 @@ Here's how to use it:
|
|||
{
|
||||
"ui:layout": [
|
||||
{
|
||||
"firstName": {"sm": 2, "md": 2, "lg": 4},
|
||||
"lastName": {"sm": 2, "md": 2, "lg": 4}
|
||||
"firstName": { "sm": 2, "md": 2, "lg": 4 },
|
||||
"lastName": { "sm": 2, "md": 2, "lg": 4 }
|
||||
},
|
||||
{"notes": {}}
|
||||
{ "notes": {} }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
![Styling_Form](images/styling_forms.png)
|
||||
|
||||
#### 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.
|
||||
|
||||
|
||||
|
||||
### 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.
|
||||
|
@ -393,17 +392,17 @@ Below is an example JSON schema that includes the numeric range field:
|
|||
"numericRange": {
|
||||
"type": "object",
|
||||
"title": "Numeric Range",
|
||||
"minimum": {
|
||||
"type": "number",
|
||||
"title": "Minimum Value"
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number",
|
||||
"title": "Maximum Value"
|
||||
}
|
||||
"minimum": {
|
||||
"type": "number",
|
||||
"title": "Minimum Value"
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number",
|
||||
"title": "Maximum Value"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### 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.
|
||||
|
@ -458,8 +456,44 @@ This structure can be represented in the form's schema as follows:
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Form Preview**:
|
||||
|
||||
![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.
|
||||
|
||||
### 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 NumericRangeField from '../rjsf/custom_widgets/NumericRangeField/NumericRangeField';
|
||||
import ObjectFieldRestrictedGridTemplate from '../rjsf/custom_templates/ObjectFieldRestrictGridTemplate';
|
||||
import CharacterCounterField from '../rjsf/custom_widgets/CharacterCounterField/CharacterCounterField';
|
||||
|
||||
enum DateCheckType {
|
||||
minimum = 'minimum',
|
||||
|
@ -57,7 +56,6 @@ export default function CustomForm({
|
|||
// set in uiSchema using the "ui:field" key for a property
|
||||
const rjsfFields: RegistryFieldsType = {
|
||||
'numeric-range': NumericRangeField,
|
||||
'character-counter': CharacterCounterField,
|
||||
};
|
||||
|
||||
const rjsfTemplates: any = {};
|
||||
|
|
|
@ -89,6 +89,19 @@ export default function BaseInputTemplate<
|
|||
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(
|
||||
label,
|
||||
schema,
|
||||
|
@ -157,6 +170,8 @@ export default function BaseInputTemplate<
|
|||
onChange={_onChange}
|
||||
onBlur={_onBlur}
|
||||
onFocus={_onFocus}
|
||||
enableCounter={enableCounter}
|
||||
maxCount={maxCount}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...inputProps}
|
||||
/>
|
||||
|
|
|
@ -59,6 +59,19 @@ function TextareaWidget<
|
|||
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 (
|
||||
<TextArea
|
||||
id={id}
|
||||
|
@ -78,6 +91,8 @@ function TextareaWidget<
|
|||
onChange={handleChange}
|
||||
invalid={commonAttributes.invalid}
|
||||
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