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', +};