From 2a52c5cc95530f7cc0557d90d2d7c7e96e07f55c Mon Sep 17 00:00:00 2001
From: Robert Hasselle <123402053+rhasselle-oddball@users.noreply.github.com>
Date: Thu, 9 May 2024 09:26:49 -0500
Subject: [PATCH] Add textUI textareaUI + mock form cleanup (#29658)
* add textUI and textareaUI
* cleanup mock form
---
.../mock-simple-forms-patterns/config/form.js | 36 +--
.../pages/mockAddress.js | 20 ++
.../pages/mockFullName.js | 21 ++
.../pages/mockSsn.js | 24 ++
...InputWidgets1.js => mockTextEmailPhone.js} | 6 +-
.../pages/mockTextInput.js | 130 +++--------
.../pages/mockTextInputAddress.js | 30 ---
.../pages/mockTextInputFullName.js | 72 ------
.../pages/mockTextInputSsn.js | 21 --
...mock-simple-forms-patterns.cypress.spec.js | 16 +-
.../tests/e2e/pagePaths.js | 8 +-
.../tests/pages/mockTextInput.unit.spec.jsx | 6 +-
.../pages/mockTextInputAddress.unit.spec.jsx | 9 +-
.../pages/mockTextInputFullName.unit.spec.jsx | 9 +-
.../pages/mockTextInputSsn.unit.spec.jsx | 2 +-
.../pages/mockTextInputWidgets1.unit.spec.jsx | 7 +-
src/platform/forms-system/src/js/types.js | 4 +-
.../checkboxGroupPattern.jsx | 3 +-
.../src/js/web-component-patterns/index.js | 1 +
.../web-component-patterns/textPatterns.jsx | 214 ++++++++++++++++++
20 files changed, 352 insertions(+), 287 deletions(-)
create mode 100644 src/applications/simple-forms/mock-simple-forms-patterns/pages/mockAddress.js
create mode 100644 src/applications/simple-forms/mock-simple-forms-patterns/pages/mockFullName.js
create mode 100644 src/applications/simple-forms/mock-simple-forms-patterns/pages/mockSsn.js
rename src/applications/simple-forms/mock-simple-forms-patterns/pages/{mockTextInputWidgets1.js => mockTextEmailPhone.js} (82%)
delete mode 100644 src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputAddress.js
delete mode 100644 src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputFullName.js
delete mode 100644 src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputSsn.js
create mode 100644 src/platform/forms-system/src/js/web-component-patterns/textPatterns.jsx
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/config/form.js b/src/applications/simple-forms/mock-simple-forms-patterns/config/form.js
index dccb37ee8b44..0d6a4423fd1f 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/config/form.js
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/config/form.js
@@ -7,11 +7,11 @@ import ConfirmationPage from '../containers/ConfirmationPage';
// pages
import chapterSelect from '../pages/chapterSelect';
import textInput from '../pages/mockTextInput';
-import textInputWidgets1 from '../pages/mockTextInputWidgets1';
+import textEmailPhone from '../pages/mockTextEmailPhone';
import numberInput from '../pages/mockNumberInput';
-import textInputFullName from '../pages/mockTextInputFullName';
-import textInputAddress from '../pages/mockTextInputAddress';
-import textInputSsn from '../pages/mockTextInputSsn';
+import fullName from '../pages/mockFullName';
+import address from '../pages/mockAddress';
+import ssn from '../pages/mockSsn';
import checkboxAndTextInput from '../pages/mockCheckboxAndTextInput';
import checkboxGroup from '../pages/mockCheckboxGroup';
import radio from '../pages/mockRadio';
@@ -103,34 +103,34 @@ const formConfig = {
schema: textInput.schema,
depends: includeChapter('textInput'),
},
- textInputWidgets1: {
+ textEmailPhone: {
path: 'text-input-widgets1',
title: 'Text Input Widgets 1', // for review page (has to be more than one word)
- uiSchema: textInputWidgets1.uiSchema,
- schema: textInputWidgets1.schema,
+ uiSchema: textEmailPhone.uiSchema,
+ schema: textEmailPhone.schema,
depends: includeChapter('textInput'),
},
- textInputFullName: {
+ fullName: {
path: 'text-input-full-name',
title: 'Text Input Full Name', // for review page (has to be more than one word)
- uiSchema: textInputFullName.uiSchema,
- schema: textInputFullName.schema,
- initialData: textInputFullName.initialData,
+ uiSchema: fullName.uiSchema,
+ schema: fullName.schema,
+ initialData: fullName.initialData,
depends: includeChapter('textInput'),
},
- textInputAddress: {
+ address: {
title: 'Text Input Address', // for review page (has to be more than one word)
path: 'text-input-address',
- uiSchema: textInputAddress.uiSchema,
- schema: textInputAddress.schema,
- initialData: textInputAddress.initialData,
+ uiSchema: address.uiSchema,
+ schema: address.schema,
+ initialData: address.initialData,
depends: includeChapter('textInput'),
},
- textInputSsn: {
+ ssn: {
title: 'SSN Pattern', // for review page (has to be more than one word)
path: 'ssn-pattern',
- uiSchema: textInputSsn.uiSchema,
- schema: textInputSsn.schema,
+ uiSchema: ssn.uiSchema,
+ schema: ssn.schema,
depends: includeChapter('textInput'),
},
},
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockAddress.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockAddress.js
new file mode 100644
index 000000000000..e9126b930ac4
--- /dev/null
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockAddress.js
@@ -0,0 +1,20 @@
+import {
+ addressSchema,
+ addressUI,
+ titleUI,
+} from 'platform/forms-system/src/js/web-component-patterns';
+
+/** @type {PageSchema} */
+export default {
+ uiSchema: {
+ ...titleUI('Web component v3 address'),
+ wcv3Address: addressUI(),
+ },
+ schema: {
+ type: 'object',
+ properties: {
+ wcv3Address: addressSchema(),
+ },
+ required: [],
+ },
+};
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockFullName.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockFullName.js
new file mode 100644
index 000000000000..144ba9016f38
--- /dev/null
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockFullName.js
@@ -0,0 +1,21 @@
+// @ts-check
+import {
+ titleUI,
+ fullNameUI,
+ fullNameSchema,
+} from 'platform/forms-system/src/js/web-component-patterns';
+
+/** @type {PageSchema} */
+export default {
+ uiSchema: {
+ ...titleUI('Web component v3 full name'),
+ wcv3SpouseFullNameNew: fullNameUI(),
+ },
+ schema: {
+ type: 'object',
+ properties: {
+ wcv3SpouseFullNameNew: fullNameSchema,
+ },
+ required: [],
+ },
+};
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockSsn.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockSsn.js
new file mode 100644
index 000000000000..e445d29973ff
--- /dev/null
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockSsn.js
@@ -0,0 +1,24 @@
+import {
+ titleUI,
+ ssnOrVaFileNumberNoHintUI,
+ ssnOrVaFileNumberNoHintSchema,
+} from 'platform/forms-system/src/js/web-component-patterns';
+
+/** @type {PageSchema} */
+export default {
+ uiSchema: {
+ ...titleUI(
+ 'Identification information',
+ 'You must enter a Social Security number or VA file number',
+ ),
+ wcv3SsnNew: ssnOrVaFileNumberNoHintUI(),
+ },
+ schema: {
+ type: 'object',
+ properties: {
+ wcv3SsnNew: ssnOrVaFileNumberNoHintSchema,
+ },
+ required: ['wcv3SsnNew'],
+ },
+ initialData: {},
+};
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputWidgets1.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextEmailPhone.js
similarity index 82%
rename from src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputWidgets1.js
rename to src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextEmailPhone.js
index ff9121e30e8d..b5bb29af4d35 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputWidgets1.js
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextEmailPhone.js
@@ -1,6 +1,4 @@
import {
- ssnUI,
- ssnSchema,
titleUI,
emailUI,
emailSchema,
@@ -11,20 +9,18 @@ import {
/** @type {PageSchema} */
export default {
uiSchema: {
- ...titleUI('Web component v3'),
+ ...titleUI('Web component v3 email and phone'),
wcv3TextEmailNew: emailUI({
description:
'By providing an email address, I agree to receive electronic correspondence from VA regarding my application',
}),
wcv3TextPhoneNew: phoneUI(),
- wcv3TextSsnNew: ssnUI(),
},
schema: {
type: 'object',
properties: {
wcv3TextEmailNew: emailSchema,
wcv3TextPhoneNew: phoneSchema,
- wcv3TextSsnNew: ssnSchema,
},
required: ['wcv3TextEmailNew'],
},
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInput.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInput.js
index 2773e01c742b..9a09d2097db3 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInput.js
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInput.js
@@ -1,12 +1,13 @@
/* eslint-disable no-unused-vars */
import React from 'react';
-import VaTextInputField from 'platform/forms-system/src/js/web-component-fields/VaTextInputField';
import {
inlineTitleSchema,
inlineTitleUI,
titleUI,
-} from 'platform/forms-system/src/js/web-component-patterns/titlePattern';
-import VaTextareaField from 'platform/forms-system/src/js/web-component-fields/VaTextareaField';
+ textUI,
+ textSchema,
+ textareaUI,
+} from 'platform/forms-system/src/js/web-component-patterns';
/** @type {PageSchema} */
export default {
@@ -14,122 +15,55 @@ export default {
// Example of using a function:
// Only use this if you need it because it will rerender every time (only on this page)
...titleUI(({ formData, formContext }) => {
- return 'RJSF';
+ return 'Web component v3 text fields';
}),
- simpleOld: {
- 'ui:title': 'TextWidget - with string description',
- 'ui:description': 'Text description',
- },
- requiredOld: {
- 'ui:title': 'TextWidget - with JSX description',
- 'ui:description': (
-
- We need the Veteran’s Social Security number or tax identification
- number to process the application when it’s submitted online, but it’s
- not a requirement to apply for the program.
-
- ),
- 'ui:errorMessages': {
- required: 'Please enter a value',
- },
- },
- disabledOld: {
- 'ui:title': 'TextWidget - disabled',
- 'ui:disabled': true,
- },
- 'view:wcv3Title': inlineTitleUI('Web component v3'),
- wcv3SimpleNew: {
- 'ui:title': 'VaTextInputField - with string description',
- 'ui:webComponentField': VaTextInputField,
- 'ui:description': 'Text description',
- },
- wcv3RequiredNew: {
- 'ui:title': 'VaTextInputField - with JSX description',
- 'ui:webComponentField': VaTextInputField,
- 'ui:description': (
+ wcv3SimpleNew: textUI({
+ title: 'VaTextInputField - with string description',
+ description: 'Text description',
+ }),
+ wcv3RequiredNew: textUI({
+ title: 'VaTextInputField - with JSX description',
+ description: (
We need the Veteran’s Social Security number or tax identification
number to process the application when it’s submitted online, but it’s
not a requirement to apply for the program.
),
- 'ui:errorMessages': {
+ errorMessages: {
required: 'Please enter a value',
},
- 'ui:options': {
- hideIf: formData => formData.hide,
- hideOnReview: true,
- },
- },
- wcv3HintNew: {
- 'ui:title': 'VaTextInputField - with string hint',
- 'ui:webComponentField': VaTextInputField,
- 'ui:options': {
- hint: 'This is a hint',
- },
- },
- wcv3InputmodeDecimalNew: {
- 'ui:title': 'VaTextInputField - with decimal inputmode',
- 'ui:webComponentField': VaTextInputField,
- 'ui:options': {
- inputmode: 'decimal',
- },
- },
- wcv3TextAreaNew: {
- 'ui:title': 'VaTextareaField (short hint)',
- 'ui:description': 'Text description',
- 'ui:webComponentField': VaTextareaField,
- 'ui:options': {
- charcount: true,
- hint: 'Normal hint',
- },
- },
- wcv3DisabledNew: {
- 'ui:title': 'VaTextInputField - disabled',
- 'ui:description': (
-
- v3 does not support disabled fields. Solve with better pattern
- instead, for example one question per page.
-
- ),
- 'ui:webComponentField': VaTextInputField,
- 'ui:disabled': true, // not supported for v3
- },
+ hideIf: formData => formData.hide,
+ hideOnReview: true,
+ }),
+ wcv3HintNew: textUI({
+ title: 'VaTextInputField with charcount',
+ hint: 'This is a hint',
+ width: 'md',
+ charcount: true,
+ }),
+ wcv3TextAreaNew: textareaUI({
+ title: 'VaTextareaField',
+ description: 'Text description',
+ hint: 'Normal hint',
+ charcount: true,
+ }),
},
schema: {
type: 'object',
properties: {
- simpleOld: {
- type: 'string',
- },
- requiredOld: {
- type: 'string',
- },
- disabledOld: {
- type: 'string',
- },
- 'view:wcv3Title': inlineTitleSchema,
- wcv3SimpleNew: {
- type: 'string',
- },
- wcv3RequiredNew: {
- type: 'string',
- },
+ wcv3SimpleNew: textSchema,
+ wcv3RequiredNew: textSchema,
wcv3HintNew: {
type: 'string',
- },
- wcv3InputmodeDecimalNew: {
- type: 'string',
+ maxLength: 24,
},
wcv3TextAreaNew: {
type: 'string',
maxLength: 30,
minLength: 10,
},
- wcv3DisabledNew: {
- type: 'string',
- },
},
- required: ['requiredOld', 'wcv3RequiredNew', 'wcv3TextAreaNew'],
+ required: ['wcv3RequiredNew', 'wcv3TextAreaNew'],
},
};
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputAddress.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputAddress.js
deleted file mode 100644
index 544dd380f5cb..000000000000
--- a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputAddress.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import addressUiSchema from 'platform/forms-system/src/js/definitions/profileAddress';
-import {
- addressSchema,
- addressUI,
- inlineTitleSchema,
- inlineTitleUI,
- titleUI,
-} from 'platform/forms-system/src/js/web-component-patterns';
-import fullSchema from 'vets-json-schema/dist/26-4555-schema.json';
-
-/** @type {PageSchema} */
-export default {
- uiSchema: {
- ...titleUI('RJSF'),
- addressOld: {
- ...addressUiSchema('addressOld', undefined, () => true),
- },
- 'view:wcv3Title': inlineTitleUI('Web component'),
- wcv3Address: addressUI(),
- },
- schema: {
- type: 'object',
- properties: {
- addressOld: fullSchema.properties.veteran.properties.address,
- 'view:wcv3Title': inlineTitleSchema,
- wcv3Address: addressSchema(),
- },
- required: [],
- },
-};
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputFullName.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputFullName.js
deleted file mode 100644
index 58af88f384b3..000000000000
--- a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputFullName.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// @ts-check
-import fullNameOldUI from 'platform/forms/definitions/fullName';
-import {
- titleUI,
- fullNameUI,
- fullNameSchema,
- inlineTitleUI,
- inlineTitleSchema,
-} from 'platform/forms-system/src/js/web-component-patterns';
-
-const fullNameDef = {
- type: 'object',
- properties: {
- first: {
- type: 'string',
- minLength: 1,
- maxLength: 30,
- },
- middle: {
- type: 'string',
- },
- last: {
- type: 'string',
- minLength: 1,
- maxLength: 30,
- },
- suffix: {
- type: 'string',
- enum: ['Jr.', 'Sr.', 'II', 'III', 'IV'],
- },
- },
- required: ['first', 'last'],
-};
-
-/** @type {PageSchema} */
-export default {
- uiSchema: {
- ...titleUI('RJSF'),
- spouseFullNameOld: {
- ...fullNameOldUI,
- first: {
- 'ui:title': 'TextWidget - Spouse\u2019s first name',
- 'ui:errorMessages': {
- required: 'Please enter a first name',
- },
- },
- last: {
- 'ui:title': 'TextWidget - Spouse\u2019s last name',
- 'ui:errorMessages': {
- required: 'Please enter a last name',
- },
- },
- middle: {
- 'ui:title': 'TextWidget - Spouse\u2019s middle name',
- },
- suffix: {
- 'ui:title': 'Select - Spouse\u2019s suffix',
- },
- },
- 'view:wcv3Title': inlineTitleUI('Web component v3'),
- wcv3SpouseFullNameNew: fullNameUI(),
- },
- schema: {
- type: 'object',
- properties: {
- spouseFullNameOld: fullNameDef,
- 'view:wcv3Title': inlineTitleSchema,
- wcv3SpouseFullNameNew: fullNameSchema,
- },
- required: [],
- },
-};
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputSsn.js b/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputSsn.js
deleted file mode 100644
index 3a2bbe04484e..000000000000
--- a/src/applications/simple-forms/mock-simple-forms-patterns/pages/mockTextInputSsn.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import {
- ssnOrVaFileNumberUI,
- ssnOrVaFileNumberSchema,
- titleUI,
-} from 'platform/forms-system/src/js/web-component-patterns';
-
-/** @type {PageSchema} */
-export default {
- uiSchema: {
- ...titleUI('Web component v3'),
- wcv3SsnNew: ssnOrVaFileNumberUI(),
- },
- schema: {
- type: 'object',
- properties: {
- wcv3SsnNew: ssnOrVaFileNumberSchema,
- },
- required: ['wcv3SsnNew'],
- },
- initialData: {},
-};
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/mock-simple-forms-patterns.cypress.spec.js b/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/mock-simple-forms-patterns.cypress.spec.js
index 19aa66876137..5ad4451d0082 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/mock-simple-forms-patterns.cypress.spec.js
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/mock-simple-forms-patterns.cypress.spec.js
@@ -27,27 +27,13 @@ const testConfig = createTestConfig(
introductionPageFlow();
});
},
- [pagePaths.textInputAddress]: ({ afterHook }) => {
+ [pagePaths.address]: ({ afterHook }) => {
cy.injectAxeThenAxeCheck();
afterHook(() => {
cy.get('@testData').then(data => {
// widgets
cy.fillPage();
// fillPage doesn't catch state select, so select state manually
- cy.get('select#root_addressOld_state').select(
- data.addressOld.state,
- );
- if (data.addressOld.city) {
- if (data.addressOld.isMilitary) {
- // there is a select dropdown instead when military is checked
- cy.get('select#root_addressOld_city').select(
- data.addressOld.city,
- );
- } else {
- cy.get('#root_addressOld_city').type(data.addressOld.city);
- }
- }
-
selectDropdownWebComponent(
`wcv3Address_state`,
data.wcv3Address.state,
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/pagePaths.js b/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/pagePaths.js
index 99ea28c0e177..222dce687906 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/pagePaths.js
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/tests/e2e/pagePaths.js
@@ -3,10 +3,10 @@ import formConfig from '../../config/form';
const formChapters = formConfig.chapters;
export default {
textInput: formChapters.textInput.pages.textInput.path,
- textInputWidgets1: formChapters.textInput.pages.textInputWidgets1.path,
- textInputFullName: formChapters.textInput.pages.textInputFullName.path,
- textInputAddress: formChapters.textInput.pages.textInputAddress.path,
- textInputSsn: formChapters.textInput.pages.textInputSsn.path,
+ textEmailPhone: formChapters.textInput.pages.textEmailPhone.path,
+ fullName: formChapters.textInput.pages.fullName.path,
+ address: formChapters.textInput.pages.address.path,
+ ssn: formChapters.textInput.pages.ssn.path,
checkboxAndTextInput: formChapters.checkbox.pages.checkboxAndTextInput.path,
checkboxGroup: formChapters.checkbox.pages.checkboxGroup.path,
numberInput: formChapters.numberInput.pages.numberInput.path,
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInput.unit.spec.jsx b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInput.unit.spec.jsx
index 02e23ec7d804..94979ce6c105 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInput.unit.spec.jsx
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInput.unit.spec.jsx
@@ -10,7 +10,7 @@ const { schema, uiSchema } = formConfig.chapters.textInput.pages.textInput;
const pageTitle = 'mock text inputs';
-const expectedNumberOfWebComponentFields = 6;
+const expectedNumberOfWebComponentFields = 4;
testNumberOfWebComponentFields(
formConfig,
schema,
@@ -28,7 +28,7 @@ testNumberOfErrorsOnSubmitForWebComponents(
pageTitle,
);
-const expectedNumberOfFields = 3;
+const expectedNumberOfFields = 0;
testNumberOfFields(
formConfig,
schema,
@@ -37,7 +37,7 @@ testNumberOfFields(
pageTitle,
);
-const expectedNumberOfErrors = 1;
+const expectedNumberOfErrors = 0;
testNumberOfErrorsOnSubmit(
formConfig,
schema,
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputAddress.unit.spec.jsx b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputAddress.unit.spec.jsx
index 741b1a1d8b3f..986e4aa1d34e 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputAddress.unit.spec.jsx
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputAddress.unit.spec.jsx
@@ -6,10 +6,7 @@ import {
} from '../../../shared/tests/pages/pageTests.spec';
import formConfig from '../../config/form';
-const {
- schema,
- uiSchema,
-} = formConfig.chapters.textInput.pages.textInputAddress;
+const { schema, uiSchema } = formConfig.chapters.textInput.pages.address;
const pageTitle = 'mock address inputs';
@@ -31,7 +28,7 @@ testNumberOfErrorsOnSubmitForWebComponents(
pageTitle,
);
-const expectedNumberOfFields = 8;
+const expectedNumberOfFields = 0;
testNumberOfFields(
formConfig,
schema,
@@ -40,7 +37,7 @@ testNumberOfFields(
pageTitle,
);
-const expectedNumberOfErrors = 4;
+const expectedNumberOfErrors = 0;
testNumberOfErrorsOnSubmit(
formConfig,
schema,
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputFullName.unit.spec.jsx b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputFullName.unit.spec.jsx
index c72ca52fc46f..a1aea27d9cb9 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputFullName.unit.spec.jsx
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputFullName.unit.spec.jsx
@@ -6,10 +6,7 @@ import {
} from '../../../shared/tests/pages/pageTests.spec';
import formConfig from '../../config/form';
-const {
- schema,
- uiSchema,
-} = formConfig.chapters.textInput.pages.textInputFullName;
+const { schema, uiSchema } = formConfig.chapters.textInput.pages.fullName;
const pageTitle = 'mock full name inputs';
@@ -31,7 +28,7 @@ testNumberOfErrorsOnSubmitForWebComponents(
pageTitle,
);
-const expectedNumberOfFields = 4;
+const expectedNumberOfFields = 0;
testNumberOfFields(
formConfig,
schema,
@@ -40,7 +37,7 @@ testNumberOfFields(
pageTitle,
);
-const expectedNumberOfErrors = 2;
+const expectedNumberOfErrors = 0;
testNumberOfErrorsOnSubmit(
formConfig,
schema,
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputSsn.unit.spec.jsx b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputSsn.unit.spec.jsx
index be8dd847c1f1..3f293c044203 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputSsn.unit.spec.jsx
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputSsn.unit.spec.jsx
@@ -4,7 +4,7 @@ import {
} from '../../../shared/tests/pages/pageTests.spec';
import formConfig from '../../config/form';
-const { schema, uiSchema } = formConfig.chapters.textInput.pages.textInputSsn;
+const { schema, uiSchema } = formConfig.chapters.textInput.pages.ssn;
const pageTitle = 'mock ssn inputs';
diff --git a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputWidgets1.unit.spec.jsx b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputWidgets1.unit.spec.jsx
index 1e9e968ddde3..e5fcf99e73c0 100644
--- a/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputWidgets1.unit.spec.jsx
+++ b/src/applications/simple-forms/mock-simple-forms-patterns/tests/pages/mockTextInputWidgets1.unit.spec.jsx
@@ -4,14 +4,11 @@ import {
} from '../../../shared/tests/pages/pageTests.spec';
import formConfig from '../../config/form';
-const {
- schema,
- uiSchema,
-} = formConfig.chapters.textInput.pages.textInputWidgets1;
+const { schema, uiSchema } = formConfig.chapters.textInput.pages.textEmailPhone;
const pageTitle = 'mock text input widgets';
-const expectedNumberOfWebComponentFields = 3;
+const expectedNumberOfWebComponentFields = 2;
testNumberOfWebComponentFields(
formConfig,
schema,
diff --git a/src/platform/forms-system/src/js/types.js b/src/platform/forms-system/src/js/types.js
index 6a7925b0caf8..73fe4b145e94 100644
--- a/src/platform/forms-system/src/js/types.js
+++ b/src/platform/forms-system/src/js/types.js
@@ -253,7 +253,7 @@
/**
* @typedef {Object} UIOptions
* @property {string} [ariaDescribedby] The id of the element that describes the field. Use `messageAriaDescribedby` for web components.
- * @property {boolean} [charcount] Whether a web-component should show a character count message. Has no effect without maxlength being set.
+ * @property {boolean} [charcount] Whether a web-component should show a character count message. Has no effect without `maxLength` being set in the schema.
* @property {string} [classNames] additional CSS classes to add to the field
* @property {boolean} [confirmRemove] For arrays. If true, will show a confirmation modal when removing an item.
* @property {string} [confirmRemoveDescription] For arrays. Description for the confirmation modal when removing an item.
@@ -280,7 +280,7 @@
* @property {string} [hint] The hint text for the field. For web components.
* @property {boolean} [includeRequiredLabelInTitle]
* @property {Array<(input) => string>} [inputTransformers]
- * @property {'number' | 'text' | 'email' | 'search' | 'tel' | 'url' | OrAnyString} [inputType] HTML input 'type' attribute. May result in different keyboard for mobile users.
+ * @property {'number' | 'text' | 'email' | 'search' | 'tel' | 'url' | OrAnyString} [inputType] Keyboard type for mobile users. Equivalent to HTML input 'type' attribute.
* @property {(item: any) => string} [itemAriaLabel] for arrays
* @property {string} [itemName] The name of the item - for arrays. For example a value of 'Child' will result in 'Add another child', 'New child', and if 'using confirmRemove', 'Are you sure you want to remove this child item?', 'Yes, remove this child item'.
* @property {boolean} [invalid] For web components. Whether or not aria-invalid will be set on the inner input. Useful when composing the component into something larger, like a date component.
diff --git a/src/platform/forms-system/src/js/web-component-patterns/checkboxGroupPattern.jsx b/src/platform/forms-system/src/js/web-component-patterns/checkboxGroupPattern.jsx
index 4338f8cbe49f..965640a1dc08 100644
--- a/src/platform/forms-system/src/js/web-component-patterns/checkboxGroupPattern.jsx
+++ b/src/platform/forms-system/src/js/web-component-patterns/checkboxGroupPattern.jsx
@@ -85,7 +85,8 @@ export const checkboxGroupUI = ({
throw new Error(
`"required" property should be explicitly set for checkboxGroupUI for
title: "${title}". Please set "required" to a boolean, or a function
- that returns a boolean.`,
+ that returns a boolean. Also you will still need to set required in
+ the schema as well.`,
);
}
if (!labels) {
diff --git a/src/platform/forms-system/src/js/web-component-patterns/index.js b/src/platform/forms-system/src/js/web-component-patterns/index.js
index c92f73b746a9..90db409c80c8 100644
--- a/src/platform/forms-system/src/js/web-component-patterns/index.js
+++ b/src/platform/forms-system/src/js/web-component-patterns/index.js
@@ -12,5 +12,6 @@ export * from './radioPattern';
export * from './arnPattern';
export * from './selectPattern';
export * from './ssnPattern';
+export * from './textPatterns';
export * from './titlePattern';
export * from './yesNoPattern';
diff --git a/src/platform/forms-system/src/js/web-component-patterns/textPatterns.jsx b/src/platform/forms-system/src/js/web-component-patterns/textPatterns.jsx
new file mode 100644
index 000000000000..ed2e0a76820b
--- /dev/null
+++ b/src/platform/forms-system/src/js/web-component-patterns/textPatterns.jsx
@@ -0,0 +1,214 @@
+import { VaTextInputField, VaTextareaField } from '../web-component-fields';
+
+/**
+ * Web component v3 uiSchema for generic text field
+ *
+ * Considerations:
+ * - Can we instead use a specific text pattern?
+ * - phoneUI
+ * - emailUI
+ * - numberUI
+ * - Do mobile users need to use a different `inputType` keyboard?
+ * - Can we provide `autocomplete`?
+ *
+ * Usage uiSchema:
+ * ```js
+ * exampleText: textUI('Simple text field')
+ * exampleText: textUI({
+ * title: 'Text field',
+ * hint: 'This is a hint',
+ * description: 'This is a description',
+ * charcount: true, // Used with minLength and maxLength in the schema
+ * })
+ * ```
+ *
+ * Usage schema:
+ * ```js
+ * exampleText: textSchema,
+ * required: ['exampleText']
+ *
+ * // or
+ * exampleText: {
+ * type: 'string',
+ * minLength: 10,
+ * maxLength: 30,
+ * }
+ * ```
+ *
+ * About `useFormsPattern`:
+ *
+ * Used with `formDescription`, `formHeading`, and `formHeadingLevel`
+ * when the label of the field should be the actual form title and
+ * have a description with JSX that should be read out by screen readers.
+ *
+ * @param {UIOptions & {
+ * title?: UISchemaOptions['ui:title'],
+ * description?: UISchemaOptions['ui:description'],
+ * hint?: UIOptions['hint'],
+ * charcount?: UIOptions['charcount'],
+ * width?: UIOptions['width'],
+ * errorMessages?: UISchemaOptions['ui:errorMessages'],
+ * messageAriaDescribedby?: UIOptions['messageAriaDescribedby'],
+ * inputType?: UIOptions['inputType'],
+ * autocomplete?: UISchemaOptions['ui:autocomplete'],
+ * }} stringOrOptions
+ * @returns {UISchemaOptions}
+ */
+export const textUI = stringOrOptions => {
+ if (typeof stringOrOptions === 'string') {
+ return {
+ 'ui:title': stringOrOptions,
+ 'ui:webComponentField': VaTextInputField,
+ };
+ }
+
+ const {
+ title,
+ description,
+ errorMessages,
+ required,
+ autocomplete,
+ validations,
+ reviewField,
+ hidden,
+ ...uiOptions
+ } = stringOrOptions;
+
+ return {
+ 'ui:title': title,
+ 'ui:description': description,
+ 'ui:required': required,
+ 'ui:webComponentField': VaTextInputField,
+ 'ui:reviewField': reviewField,
+ 'ui:autocomplete': autocomplete,
+ 'ui:hidden': hidden,
+ 'ui:validations': validations,
+ 'ui:options': {
+ ...uiOptions,
+ },
+ 'ui:errorMessages': errorMessages,
+ };
+};
+
+/**
+ * Schema for generic text field
+ *
+ * If you need to use `maxLength` or `minLength`, just define manually rather than use this.
+ *
+ * ```js
+ * exampleText: {
+ * type: 'string',
+ * minLength: 10,
+ * maxLength: 30,
+ * }
+ * ```
+ */
+export const textSchema = {
+ type: 'string',
+};
+
+/**
+ * Web component v3 uiSchema for generic textarea field
+ *
+ * Usage uiSchema:
+ * ```js
+ * exampleText: textareaUI('Simple textarea field')
+ * exampleText: textareaUI({
+ * title: 'Textarea field',
+ * hint: 'This is a hint',
+ * description: 'This is a description',
+ * charcount: true, // Used with minLength and maxLength in the schema
+ * })
+ * ```
+ *
+ * Usage schema:
+ * ```js
+ * exampleText: textareaSchema,
+ * required: ['exampleText']
+ *
+ * // or
+ * exampleText: {
+ * type: 'string',
+ * minLength: 10,
+ * maxLength: 30,
+ * }
+ * ```
+ *
+ * About `labelHeaderLevel`:
+ *
+ * Simply use the label as the form header.
+ *
+ * About `useFormsPattern`:
+ *
+ * Advanced version of `labelHeaderLevel`.
+ * Used with `formDescription`, `formHeading`, and `formHeadingLevel`
+ * when the label of the field should be the actual form title and
+ * have a description with JSX that should be read out by screen readers.
+ *
+ * @param {UIOptions & {
+ * title?: UISchemaOptions['ui:title'],
+ * description?: UISchemaOptions['ui:description'],
+ * hint?: UIOptions['hint'],
+ * charcount?: UIOptions['charcount'],
+ * errorMessages?: UISchemaOptions['ui:errorMessages'],
+ * labelHeaderLevel?: UIOptions['labelHeaderLevel'],
+ * messageAriaDescribedby?: UIOptions['messageAriaDescribedby'],
+ * useFormsPattern?: UIOptions['useFormsPattern'],
+ * formHeading?: UIOptions['formHeading'],
+ * formDescription?: UIOptions['formDescription'],
+ * formHeadingLevel?: UIOptions['formHeadingLevel'],
+ * }} stringOrOptions
+ * @returns {UISchemaOptions}
+ */
+export const textareaUI = stringOrOptions => {
+ if (typeof stringOrOptions === 'string') {
+ return {
+ 'ui:title': stringOrOptions,
+ 'ui:webComponentField': VaTextareaField,
+ };
+ }
+
+ const {
+ title,
+ description,
+ errorMessages,
+ required,
+ autocomplete,
+ validations,
+ reviewField,
+ hidden,
+ ...uiOptions
+ } = stringOrOptions;
+
+ return {
+ 'ui:title': title,
+ 'ui:description': description,
+ 'ui:required': required,
+ 'ui:webComponentField': VaTextareaField,
+ 'ui:reviewField': reviewField,
+ 'ui:hidden': hidden,
+ 'ui:autocomplete': autocomplete,
+ 'ui:validations': validations,
+ 'ui:options': {
+ ...uiOptions,
+ },
+ 'ui:errorMessages': errorMessages,
+ };
+};
+
+/**
+ * Schema for generic textarea field
+ *
+ * If you need to use `maxLength` or `minLength`, just define manually rather than use this.
+ *
+ * ```js
+ * exampleText: {
+ * type: 'string',
+ * minLength: 10,
+ * maxLength: 30,
+ * }
+ * ```
+ */
+export const textareaSchema = {
+ type: 'string',
+};