diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/ReservedProperties.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/ReservedProperties.java index bd9ee8f4b7..8669b314b5 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/ReservedProperties.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/form/ReservedProperties.java @@ -152,6 +152,7 @@ private ReservedProperties() { public static final String PN_SHOW_AS_POPUP = "showAsPopup"; public static final String PN_TEXT_IS_RICH = "textIsRich"; public static final String PN_MULTILINE = "multiLine"; + public static final String PN_SHOW_CHARACTER_COUNT = "showCharacterCount"; public static final String PN_DESIGN_DEFAULT_TYPE = Title.PN_DESIGN_DEFAULT_TYPE; public static final String PN_TITLE_LINK_DISABLED = Title.PN_TITLE_LINK_DISABLED; public static final String PN_DRAG_DROP_TEXT = "dragDropText"; @@ -173,6 +174,7 @@ private ReservedProperties() { public static final String FD_DIALOG_LABEL = "fd:dialogLabel"; public static final String FD_XFA_SCRIPTS = "fd:xfaScripts"; public static final String FD_FILE_ACCEPT_EXTENSIONS = "fd:acceptExtensions"; + public static final String FD_SHOW_CHARACTER_COUNT = "fd:showCharacterCount"; public static final String FD_DRAFT_ID = "fd:draftId"; public static final String FD_CHANGE_EVENT_BEHAVIOUR = "fd:changeEventBehaviour"; diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImpl.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImpl.java index 150f6bc9fc..ac67061d46 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImpl.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImpl.java @@ -16,6 +16,7 @@ package com.adobe.cq.forms.core.components.internal.models.v1.form; import java.util.Date; +import java.util.Map; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -27,6 +28,7 @@ import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; +import org.jetbrains.annotations.NotNull; import com.adobe.cq.export.json.ComponentExporter; import com.adobe.cq.export.json.ExporterConstants; @@ -53,6 +55,10 @@ public class TextInputImpl extends AbstractFieldImpl implements TextInput { @Default(booleanValues = false) protected boolean multiLine; + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_SHOW_CHARACTER_COUNT) + @Nullable + protected Boolean showCharacterCount; + @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL, name = ReservedProperties.PN_FORMAT) @Nullable protected String format; @@ -75,6 +81,13 @@ public boolean isMultiLine() { return multiLine; } + @Override + @Nullable + public Boolean isShowCharacterCount() { + // the character count is only applicable to multi line (textarea) fields + return isMultiLine() ? showCharacterCount : null; + } + @Override public String getFieldType() { if (isMultiLine()) { @@ -168,4 +181,20 @@ private void initTextInput() { minimum = null; } } + + @Override + public @NotNull Map getProperties() { + Map properties = super.getProperties(); + // custom (non-spec) property: reported under properties as fd:showCharacterCount, not as a top-level JSON + // field, mirroring how other custom properties (e.g. fd:disabledXfaScripts) are exported. Note this is a + // distinct name from the JCR-authored property (ReservedProperties.PN_SHOW_CHARACTER_COUNT, unprefixed) - + // a colon in a Granite checkbox widget's authored "name" breaks rendering in the AEM Forms authoring editor + // (confirmed empirically), so the fd: namespacing is applied only on the JSON-export side, same as + // fd:xfaScripts (JCR) vs fd:disabledXfaScripts (JSON). + Boolean showCharacterCountValue = isShowCharacterCount(); + if (showCharacterCountValue != null) { + properties.put(ReservedProperties.FD_SHOW_CHARACTER_COUNT, showCharacterCountValue); + } + return properties; + } } diff --git a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/TextInput.java b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/TextInput.java index 4415ceabde..ec978ac2d2 100644 --- a/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/TextInput.java +++ b/bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/TextInput.java @@ -43,6 +43,26 @@ default boolean isMultiLine() { return false; } + /** + * Returns {@code true} if a live character count should be shown below the field, {@code false} if it should not, or + * {@code null} if not configured. Only applicable when {@link #isMultiLine()} is {@code true}; {@code null} is returned + * for single line fields regardless of how the underlying property was set. + *

+ * This is a custom (non-spec) property, not part of the Headless Adaptive Form JSON schema, so it is not exported as a + * top-level JSON property; it is instead reported under {@code properties} as {@code fd:showCharacterCount} (see + * {@link com.adobe.cq.forms.core.components.models.form.FormComponent#getProperties()}), mirroring how other custom + * properties (e.g. {@code fd:disabledXfaScripts}) are exported. + * + * @return {@code true} if the character count should be shown, {@code false} if not, or {@code null} if not configured or + * not applicable + * @since com.adobe.cq.forms.core.components.models.form 5.13.0 + */ + @Nullable + @JsonIgnore + default Boolean isShowCharacterCount() { + return null; + } + /** * Returns {@code "off"} if autocomplete if disabled, otherwise {@code "on"} or values listed @see * here diff --git a/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImplTest.java b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImplTest.java index 1542522422..f13edf3658 100644 --- a/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImplTest.java +++ b/bundles/af-core/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TextInputImplTest.java @@ -57,6 +57,8 @@ public class TextInputImplTest { private static final String PATH_TEXTINPUT_DATALAYER = CONTENT_ROOT + "/textinput-datalayer"; private static final String PATH_TEXTINPUT_CUSTOMIZED = CONTENT_ROOT + "/textinput-customized"; private static final String PATH_TEXTINPUT_2 = CONTENT_ROOT + "/multiline-textinput"; + private static final String PATH_TEXTINPUT_CHARACTERCOUNT = CONTENT_ROOT + "/textinput-charactercount"; + private static final String PATH_TEXTINPUT_CHARACTERCOUNT_SINGLELINE = CONTENT_ROOT + "/textinput-charactercount-singleline"; private static final String PATH_NUMBER_TEXTINPUT = CONTENT_ROOT + "/number-textinput"; private static final String PATH_NUMBER_TEXTINPUT_EXCLUSIVE = CONTENT_ROOT + "/number-textinput-exclusive"; private static final String PATH_FORMAT_TEXTINPUT = CONTENT_ROOT + "/textinput-format"; @@ -259,6 +261,47 @@ void testIsMultiLine() { assertEquals(false, textInputMock.isMultiLine()); } + @Test + void testIsShowCharacterCount() { + TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CUSTOMIZED, TextInput.class, context); + assertEquals(null, textInput.isShowCharacterCount()); + TextInput textInputMock = Mockito.mock(TextInput.class); + Mockito.when(textInputMock.isShowCharacterCount()).thenCallRealMethod(); + assertEquals(null, textInputMock.isShowCharacterCount()); + } + + @Test + void testIsShowCharacterCountWhenSet() { + TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CHARACTERCOUNT, TextInput.class, context); + assertEquals(true, textInput.isShowCharacterCount()); + assertEquals(100, textInput.getMaxLength().intValue()); + } + + @Test + void testCharacterCountJSONExport() throws Exception { + TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CHARACTERCOUNT, TextInput.class, context); + Utils.testJSONExport(textInput, Utils.getTestExporterJSONPath(BASE, PATH_TEXTINPUT_CHARACTERCOUNT)); + } + + @Test + void testShowCharacterCountReportedUnderCustomProperties() { + // custom (non-spec) property: exported under properties as fd:showCharacterCount, not as a top-level JSON field + TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CHARACTERCOUNT, TextInput.class, context); + Map properties = textInput.getProperties(); + assertEquals(true, properties.get("fd:showCharacterCount")); + } + + @Test + void testIsShowCharacterCountIgnoredForSingleLine() { + // showCharacterCount is only applicable to multi line fields; a single line field must report null + // regardless of what was authored, since the feature has no effect there. + TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CHARACTERCOUNT_SINGLELINE, TextInput.class, context); + assertEquals(false, textInput.isMultiLine()); + assertEquals(null, textInput.isShowCharacterCount()); + assertFalse("showCharacterCount should not be reported for single line fields", + textInput.getProperties().containsKey("fd:showCharacterCount")); + } + @Test void testGetPlaceHolder() { TextInput textInput = Utils.getComponentUnderTest(PATH_TEXTINPUT_CUSTOMIZED, TextInput.class, context); diff --git a/bundles/af-core/src/test/resources/form/textinput/exporter-textinput-charactercount.json b/bundles/af-core/src/test/resources/form/textinput/exporter-textinput-charactercount.json new file mode 100644 index 0000000000..b493188fa8 --- /dev/null +++ b/bundles/af-core/src/test/resources/form/textinput/exporter-textinput-charactercount.json @@ -0,0 +1,20 @@ +{ + "id": "textinput-357c2ee34c", + "fieldType": "multiline-input", + "name": "abc", + "type": "string", + "maxLength": 100, + "label": { + "value": "def" + }, + "events": { + "custom:setProperty": [ + "$event.payload" + ] + }, + "properties": { + "fd:path": "/content/textinput-charactercount", + "fd:showCharacterCount": true + }, + ":type": "core/fd/components/form/textinput/v1/textinput" +} diff --git a/bundles/af-core/src/test/resources/form/textinput/test-content.json b/bundles/af-core/src/test/resources/form/textinput/test-content.json index 9fad69379c..d6c32cb1ed 100644 --- a/bundles/af-core/src/test/resources/form/textinput/test-content.json +++ b/bundles/af-core/src/test/resources/form/textinput/test-content.json @@ -234,5 +234,24 @@ "sling:resourceType" : "core/fd/components/form/textinput/v1/textinput", "name" : "abc", "jcr:title" : "def" + }, + "textinput-charactercount" : { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType" : "core/fd/components/form/textinput/v1/textinput", + "name" : "abc", + "jcr:title" : "def", + "multiLine": true, + "fieldType": "multiline-input", + "showCharacterCount": true, + "maxLength": 100 + }, + "textinput-charactercount-singleline" : { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType" : "core/fd/components/form/textinput/v1/textinput", + "name" : "abc", + "jcr:title" : "def", + "fieldType": "text-input", + "showCharacterCount": true, + "maxLength": 100 } } diff --git a/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/clientlibs/forms-clientlib-site/styles/components/adaptive-form/textinput.less b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/clientlibs/forms-clientlib-site/styles/components/adaptive-form/textinput.less index c76f76b169..2f19652f8a 100644 --- a/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/clientlibs/forms-clientlib-site/styles/components/adaptive-form/textinput.less +++ b/examples/ui.apps/src/main/content/jcr_root/apps/forms-components-examples/clientlibs/forms-clientlib-site/styles/components/adaptive-form/textinput.less @@ -3,4 +3,9 @@ } textarea.cmp-adaptiveform-textinput__widget { height: @textarea-height; +} +.cmp-adaptiveform-textinput__charcount { + .shortdescriptionMixin(); + color: @light-gray; + text-align: right; } \ No newline at end of file diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/textinput/basic/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/textinput/basic/.content.xml index 83de429cb2..3905a93e24 100644 --- a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/textinput/basic/.content.xml +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/textinput/basic/.content.xml @@ -249,6 +249,24 @@ jcr:primaryType="nt:unstructured" click="[submitForm()]"/> + diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/README.md b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/README.md index 7aaa849870..a678d69d3c 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/README.md +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/README.md @@ -46,6 +46,7 @@ The following properties are written to JCR for this Form Text component and are 12. `./minLengthMessage` - defines the minimum length error message for the field. 13. `./displayPatternType` - defines the display pattern type for formatting the input value (e.g., 'phonenumber', 'socialsecuritynumber', 'email-alphanumeric', 'zipcode') 14. `./displayValueExpression` - defines the expression used to format the display value (e.g., 'formatInput($field.$value, 'phonenumber')') +15. `./showCharacterCount` - if set to `true`, a live character count is displayed below the field. Only applicable when `./multiLine` is `true` (the dialog checkbox is hidden otherwise, and the Sling Model reports `null` for single line fields regardless of the stored value). Combine with `maxLength` to show a running total out of the limit (e.g. `12 / 100`). This is a custom (non-spec) property: it is not exported as a top-level JSON field, but reported under `properties` as `fd:showCharacterCount`, consistent with other custom properties (e.g. `fd:xfaScripts` (JCR) / `fd:disabledXfaScripts` (JSON)). Note the JSON export key intentionally differs from the JCR-authored property name here - a colon in this dialog field's authored `name` breaks Granite checkbox rendering. ### Behavior of `maxLength` and `maxLengthMessage` @@ -80,6 +81,7 @@ BLOCK cmp-adaptiveform-textinput ELEMENT cmp-adaptiveform-textinput__shortdescription ELEMENT cmp-adaptiveform-textinput__longdescription ELEMENT cmp-adaptiveform-textinput__errormessage + ELEMENT cmp-adaptiveform-textinput__charcount ``` ### Note diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/_cq_dialog/.content.xml b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/_cq_dialog/.content.xml index 109b3998e7..20642e9b12 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/_cq_dialog/.content.xml +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/_cq_dialog/.content.xml @@ -53,10 +53,19 @@ name="./default"/> + + + this.updateCharCount()); + } } } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/textinput.html b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/textinput.html index ebf4835dad..82e761d540 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/textinput.html +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/textinput/v1/textinput/textinput.html @@ -66,6 +66,9 @@ minlength="${text.minLength}" maxlength="${text.maxLength}" dir="auto"/> +

0 / ${text.maxLength}
diff --git a/ui.tests/test-module/specs/aemEmbedContainer/aemEmbedContainer.runtime.cy.js b/ui.tests/test-module/specs/aemEmbedContainer/aemEmbedContainer.runtime.cy.js index 415945ed01..40867b3a9f 100644 --- a/ui.tests/test-module/specs/aemEmbedContainer/aemEmbedContainer.runtime.cy.js +++ b/ui.tests/test-module/specs/aemEmbedContainer/aemEmbedContainer.runtime.cy.js @@ -46,7 +46,7 @@ describe("Sites with Aem Embed Container", () => { it("test for form presence in nonIframe mode", () => { cy.get('.cmp-adaptiveform-container').should('have.length', 1); - cy.get('.cmp-adaptiveform-container').find('.cmp-adaptiveform-textinput__widget').should('have.length', 13); + cy.get('.cmp-adaptiveform-container').find('.cmp-adaptiveform-textinput__widget').should('have.length', 14); }) it('Test iframe src with dataRef', () => { diff --git a/ui.tests/test-module/specs/embedFormsInSite/embedFormsInSite.runtime.cy.js b/ui.tests/test-module/specs/embedFormsInSite/embedFormsInSite.runtime.cy.js index 2ce01a5dc5..5f9abab48e 100644 --- a/ui.tests/test-module/specs/embedFormsInSite/embedFormsInSite.runtime.cy.js +++ b/ui.tests/test-module/specs/embedFormsInSite/embedFormsInSite.runtime.cy.js @@ -56,7 +56,7 @@ describe("Embed multiple form in site", () => { it("form 1 model initialized properly", () => { expect(formContainer[0], "formcontainer is initialized").to.not.be.null; - expect(Object.keys(formContainer[0]._fields).length).to.equal(14); + expect(Object.keys(formContainer[0]._fields).length).to.equal(15); }); it("form 2 model initialized properly", () => { diff --git a/ui.tests/test-module/specs/textinput/textinput.authoring.cy.js b/ui.tests/test-module/specs/textinput/textinput.authoring.cy.js index 682cdd4751..19bfdeab07 100644 --- a/ui.tests/test-module/specs/textinput/textinput.authoring.cy.js +++ b/ui.tests/test-module/specs/textinput/textinput.authoring.cy.js @@ -50,10 +50,16 @@ describe('Page - Authoring', function () { // Check If Dialog Options Are Visible cy.get("[name='./multiLine']") .should("exist"); + cy.get("[name='./showCharacterCount']") + .should("exist"); cy.get("[name='./autocomplete']") .should("exist"); // Checking some dynamic behaviours + // "Show character count" is only applicable to multi line fields; hidden until "Allow multiple lines" is checked + cy.get(".cmp-adaptiveform-textinput__showcharactercount").parent('div').invoke('css', 'display').should('equal', 'none'); + cy.get("input[name='./multiLine']").click({force: true}); + cy.get(".cmp-adaptiveform-textinput__showcharactercount").parent('div').invoke('css', 'display').should('equal', 'block'); cy.get(".cmp-adaptiveform-textinput__maxlength").invoke('css', 'display').should('equal', 'block'); cy.get(".cmp-adaptiveform-textinput__minlength").invoke('css', 'display').should('equal', 'block'); cy.get(".cmp-adaptiveform-base__placeholder").parent('div').invoke('css', 'display').should('equal', 'block'); @@ -145,6 +151,27 @@ describe('Page - Authoring', function () { testTextInputBehaviour(textInputEditPathSelector, textInputDrop); }); + it('shows a static character count placeholder in the authoring canvas', function () { + dropTextInputInContainer(); + cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + textInputEditPathSelector); + cy.invokeEditableAction("[data-action='CONFIGURE']"); + cy.get("input[name='./multiLine']").click({force: true}); + cy.get("input[name='./showCharacterCount']").click({force: true}); + cy.get('.cmp-adaptiveform-textinput__editdialog').contains('Validation').click({force: true}); + cy.get("coral-numberinput[name='./maxLength']").find("input").clear({force: true}).type("20", {force: true}); + // use submitConfigureDialog (not a plain click) so we wait for the save to be persisted + // server-side before reloading; otherwise the reload can race the dialog's save request. + cy.submitConfigureDialog(); + cy.reload(); + // in the authoring canvas the field is not interactive (typing doesn't reach the widget), so the + // counter is just the static initial value: "0 / 20" + cy.get('[data-type="Editable"][data-path="' + textInputEditPath + '"]') + .find('.cmp-adaptiveform-textinput__charcount-current').should('have.text', '0'); + cy.get('[data-type="Editable"][data-path="' + textInputEditPath + '"]') + .find('.cmp-adaptiveform-textinput__charcount-max').should('have.text', ' / 20'); + cy.deleteComponentByPath(textInputDrop); + }); + it.skip('pasted component should have unique name', function () { testCopyPasteComponent(textInputEditPathSelector, textInputEditPathSelectorCopy, textInputDrop); }); diff --git a/ui.tests/test-module/specs/textinput/textinput.runtime.cy.js b/ui.tests/test-module/specs/textinput/textinput.runtime.cy.js index cabd1cb5c0..88500b8add 100644 --- a/ui.tests/test-module/specs/textinput/textinput.runtime.cy.js +++ b/ui.tests/test-module/specs/textinput/textinput.runtime.cy.js @@ -159,6 +159,41 @@ describe("Form Runtime with Text Input", () => { }); }) + it("character count updates live and respects maxLength", () => { + const charCountWidget = "textarea[name='textcharcount']"; + const charCountCurrent = ".cmp-adaptiveform-textinput__charcount-current"; + + cy.get(charCountWidget).closest('[data-cmp-is="adaptiveFormTextInput"]').find(charCountCurrent).should('have.text', '0'); + cy.get(charCountWidget).clear().type("hello world").then(() => { + cy.get(charCountWidget).closest('[data-cmp-is="adaptiveFormTextInput"]').find(charCountCurrent).should('have.text', '11'); + }); + // typing past the field's maxLength (20); native maxlength caps the actual value, counter must reflect the capped length + cy.get(charCountWidget).type(" - this extra text pushes well past the limit").then(() => { + cy.get(charCountWidget).invoke('val').should('have.length', 20); + cy.get(charCountWidget).closest('[data-cmp-is="adaptiveFormTextInput"]').find(charCountCurrent).should('have.text', '20'); + }); + }) + + it("character count stays in sync when the value is updated programmatically (rules/prefill/reset), not just by typing", () => { + const charCountWidget = "textarea[name='textcharcount']"; + const charCountCurrent = ".cmp-adaptiveform-textinput__charcount-current"; + + cy.get(charCountWidget).closest('[data-cmp-is="adaptiveFormTextInput"]').then(($el) => { + const id = $el.attr('id'); + const model = formContainer._model.getElement(id); + + // simulate a value change coming from a rule/prefill/default-value (no DOM 'input' event is fired) + model.value = "abcdef"; + cy.get(`#${id}`).find(charCountCurrent).should('have.text', '6'); + + // simulate a reset/clear of the value + cy.wrap(null).then(() => { + model.value = ""; + cy.get(`#${id}`).find(charCountCurrent).should('have.text', '0'); + }); + }); + }) + it("should set valid to false and errorMessage other textfields on a certain string input", () => { // Rule on textbox9: When textbox9 is changed => set valid and error message property of textbox10 @@ -193,7 +228,9 @@ describe("Form Runtime with Text Input", () => { const [textbox6, textBox6FieldView] = Object.entries(formContainer._fields)[5]; const [textbox7, textBox7FieldView] = Object.entries(formContainer._fields)[6]; const [textbox8, textBox8FieldView] = Object.entries(formContainer._fields)[7]; - const [submitbutton1, fieldView] = Object.entries(formContainer._fields)[13] + const [submitbutton1, fieldView] = Object.entries(formContainer._fields).find(([id, field]) => + field._model && field._model.fieldType === 'button' + ); // 1. Required cy.get(`#${textbox6}`).find("input").focus().blur().then(x => {