Repository: vaadin/flow-components
Pull request #7243: feat: Improve FormLayoutElement API and add FormItemElement
Author: login: TatuLund
Labels: name: target/23.5
name: need to pick manually 23.5
Created: 2025-03-25T07:40:44Z | Merged: 2025-03-31T08:18:41Z

Fixes https://github.com/vaadin/testbench/issues/1886


Diff:
diff --git a/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/pom.xml b/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/pom.xml
index 30cbab0469a..bd274317ae5 100644
--- a/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/pom.xml
+++ b/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/pom.xml
@@ -82,6 +82,12 @@
             <artifactId>vaadin-text-field-flow</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.vaadin</groupId>
+            <artifactId>vaadin-text-field-testbench</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-simple</artifactId>
diff --git a/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/main/java/com/vaadin/flow/component/formlayout/test/FormLayoutView.java b/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/main/java/com/vaadin/flow/component/formlayout/test/FormLayoutView.java
index c4213211200..60982f8cd7c 100644
--- a/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/main/java/com/vaadin/flow/component/formlayout/test/FormLayoutView.java
+++ b/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/main/java/com/vaadin/flow/component/formlayout/test/FormLayoutView.java
@@ -182,6 +182,7 @@ private void createResponsiveLayout() {
 
     private void createFormLayoutWithItems() {
         FormLayout layoutWithFormItems = new FormLayout();
+        layoutWithFormItems.setId("form-layout-form-items");
 
         TextField firstName = new TextField();
         firstName.setPlaceholder("John");
@@ -230,6 +231,8 @@ private void createLayoutHandleColspans() {
 
     private void createFormLayoutWithBinder() {
         FormLayout layoutWithBinder = new FormLayout();
+        layoutWithBinder.setId("binder-form-layout");
+
         Binder<Contact> binder = new Binder<>();
 
         // The object that will be edited
@@ -255,6 +258,7 @@ private void createFormLayoutWithBinder() {
         layoutWithBinder.addFormItem(birthDate, "Birthdate");
         layoutWithBinder.addFormItem(email, "E-mail");
         FormItem phoneItem = layoutWithBinder.addFormItem(phone, "Phone");
+        phoneItem.setId("phone-item");
         phoneItem.add(doNotCall);
 
         // Button bar
diff --git a/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/test/java/com/vaadin/flow/component/formlayout/tests/FormLayoutIT.java b/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/test/java/com/vaadin/flow/component/formlayout/tests/FormLayoutIT.java
index 9ddf3904393..cbe697f12f4 100644
--- a/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/test/java/com/vaadin/flow/component/formlayout/tests/FormLayoutIT.java
+++ b/vaadin-form-layout-flow-parent/vaadin-form-layout-flow-integration-tests/src/test/java/com/vaadin/flow/component/formlayout/tests/FormLayoutIT.java
@@ -24,6 +24,9 @@
 import org.openqa.selenium.Dimension;
 import org.openqa.selenium.WebElement;
 
+import com.vaadin.flow.component.formlayout.testbench.FormItemElement;
+import com.vaadin.flow.component.formlayout.testbench.FormLayoutElement;
+import com.vaadin.flow.component.textfield.testbench.TextFieldElement;
 import com.vaadin.flow.testutil.TestPath;
 import com.vaadin.tests.AbstractComponentIT;
 
@@ -47,9 +50,9 @@ public void init() {
      * you resize the browser window
      */
     public void custom_responsive_layouting() {
-        WebElement firstLayout = findElement(By.tagName("vaadin-form-layout"));
-        List<WebElement> textFields = firstLayout
-                .findElements(By.tagName("vaadin-text-field"));
+        FormLayoutElement firstLayout = $(FormLayoutElement.class).first();
+        List<TextFieldElement> textFields = firstLayout
+                .$(TextFieldElement.class).all();
         Assert.assertEquals(3, textFields.size());
 
         // 3 columns, all should be horizontally aligned (tolerance of some
@@ -92,10 +95,16 @@ public void form_with_binder() {
                         .equals(info.getText()));
 
         // Fill form: there shouldn't be an error
-        $("vaadin-text-field").id("binder-first-name").sendKeys("foo");
-        $("vaadin-text-field").id("binder-last-name").sendKeys("bar");
-        $("vaadin-text-field").id("binder-phone").sendKeys("123-456-789");
-        $("vaadin-text-field").id("binder-email").sendKeys("example@foo.bar");
+        FormLayoutElement formLayout = $(FormLayoutElement.class)
+                .id("binder-form-layout");
+
+        formLayout.getFormItemByLabel("First name").getField().sendKeys("foo");
+        formLayout.getFormItemByLabel("Last name").getField().sendKeys("bar");
+        formLayout.$(FormItemElement.class).id("phone-item")
+                .$(TextFieldElement.class).first().setValue("123-456-789");
+        formLayout.getFormItemByLabel("E-mail").getField()
+                .sendKeys("example@foo.bar");
+
         setValue("binder-birth-date", "2003-01-02");
         setChecked("binder-do-not-call", true);
         forceClick(save);
diff --git a/vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java b/vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
new file mode 100644
index 00000000000..8d988b7b7a5
--- /dev/null
+++ b/vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.formlayout.testbench;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a {@code <vaadin-form-item>} element.
+ */
+@Element("vaadin-form-item")
+public class FormItemElement extends TestBenchElement {
+
+    /**
+     * Gets the label element from the label slot.
+     *
+     * @return the label element as a TestBenchElement or {@code null} if the
+     *         form item contains no label.
+     */
+    public TestBenchElement getLabel() {
+        return (TestBenchElement) executeScript("""
+                return arguments[0].querySelector(':scope > [slot="label"]');
+                """, this);
+    }
+
+    /**
+     * Gets the text of the label element.
+     *
+     * @return the text of the label element or {@code null} if the form item
+     *         contains no label.
+     */
+    public String getLabelText() {
+        var label = getLabel();
+        return label != null ? label.getText() : null;
+    }
+
+    /**
+     * Gets the field element.
+     *
+     * @return the field element as a TestBenchElement or {@code null} if the
+     *         form item contains no field.
+     */
+    public TestBenchElement getField() {
+        return (TestBenchElement) executeScript(
+                """
+                        return [...arguments[0].children].find((child) => child.validate || child.checkValidity);
+                        """,
+                this);
+    }
+}
diff --git a/vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormLayoutElement.java b/vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormLayoutElement.java
index bce1d0e1993..2ac4b20a849 100644
--- a/vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormLayoutElement.java
+++ b/vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormLayoutElement.java
@@ -15,6 +15,8 @@
  */
 package com.vaadin.flow.component.formlayout.testbench;
 
+import java.util.List;
+
 import com.vaadin.testbench.TestBenchElement;
 import com.vaadin.testbench.elementsbase.Element;
 
@@ -24,4 +26,26 @@
  */
 @Element("vaadin-form-layout")
 public class FormLayoutElement extends TestBenchElement {
+
+    /**
+     * Retrieves all form items of type {@link FormItemElement}.
+     *
+     * @return a list of all {@link FormItemElement} instances.
+     */
+    public List<FormItemElement> getFormItems() {
+        return $(FormItemElement.class).all();
+    }
+
+    /**
+     * Retrieves a form item element by its label.
+     *
+     * @param label
+     *            the label of the form item to retrieve
+     * @return the form item element or {@code null} if not found
+     */
+    public FormItemElement getFormItemByLabel(String label) {
+        return getFormItems().stream()
+                .filter(item -> label.equals(item.getLabelText())).findFirst()
+                .orElse(null);
+    }
 }


Reviews:
user: login: vursen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T07:13:37Z
user: login: TatuLund
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T13:15:00Z
user: login: TatuLund
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T13:18:06Z
user: login: vursen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T13:47:55Z
user: login: vursen
body: Please update the JavaDoc to reflect the changes.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T14:22:14Z
user: login: vursen
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T08:06:26Z

Review comments:
diff_hunk: @@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.formlayout.testbench;
+
+import java.util.Objects;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a <code>&lt;vaadin-form-item&gt;</code>
+ * element.
+ */
+@Element("vaadin-form-item")
+public class FormItemElement extends TestBenchElement {
+
+    /**
+     * Retrieves the label element from the form items label slot.
+     *
+     * @return the label element as a TestBenchElement
+     */
+    public TestBenchElement getLabel() {
+        return (TestBenchElement) executeScript(
+                "const formItem = arguments[0];"
+                        + "return Array.from(formItem.children).filter("
+                        + "el => el.getAttribute('slot') === 'label'" + ")[0];",
+                this);
path: vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
user: login: vursen
body: ```suggestion
        return getPropertyElements("children").stream().filter(
                (element) -> "label".equals(element.getDomAttribute("slot")))
                .findFirst().orElse(null);
```
created_at: 2025-03-27T06:15:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.formlayout.testbench;
+
+import java.util.Objects;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a <code>&lt;vaadin-form-item&gt;</code>
+ * element.
+ */
+@Element("vaadin-form-item")
+public class FormItemElement extends TestBenchElement {
+
+    /**
+     * Retrieves the label element from the form items label slot.
+     *
+     * @return the label element as a TestBenchElement
+     */
+    public TestBenchElement getLabel() {
+        return (TestBenchElement) executeScript(
+                "const formItem = arguments[0];"
+                        + "return Array.from(formItem.children).filter("
+                        + "el => el.getAttribute('slot') === 'label'" + ")[0];",
+                this);
+    }
+
+    /**
+     * Retrieves the text of the label element associated with this form item.
+     *
+     * @return the text of the label element
+     * @throws NullPointerException
+     *             if the label element is not found
+     */
+    public String getLabelText() {
+        var label = getLabel();
+        Objects.requireNonNull(label, "Label element not found");
+        return label.getText();
path: vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
user: login: vursen
body: What is the benefit of throwing an exception rather than returning `null`?

```suggestion
        var label = getLabel();
        return label != null ? label.getText() : null;
```
created_at: 2025-03-27T06:17:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.formlayout.testbench;
+
+import java.util.Objects;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a <code>&lt;vaadin-form-item&gt;</code>
+ * element.
+ */
+@Element("vaadin-form-item")
+public class FormItemElement extends TestBenchElement {
+
+    /**
+     * Retrieves the label element from the form items label slot.
+     *
+     * @return the label element as a TestBenchElement
+     */
+    public TestBenchElement getLabel() {
+        return (TestBenchElement) executeScript(
+                "const formItem = arguments[0];"
+                        + "return Array.from(formItem.children).filter("
+                        + "el => el.getAttribute('slot') === 'label'" + ")[0];",
+                this);
+    }
+
+    /**
+     * Retrieves the text of the label element associated with this form item.
+     *
+     * @return the text of the label element
+     * @throws NullPointerException
+     *             if the label element is not found
+     */
+    public String getLabelText() {
+        var label = getLabel();
+        Objects.requireNonNull(label, "Label element not found");
+        return label.getText();
+    }
+
+    /**
+     * Retrieves the input element associated with the label of this form item.
+     *
+     * @return the input element associated with the label of this form item.
+     */
+    public TestBenchElement getInput() {
+        String id = getLabel().getAttribute("id");
+        return $(TestBenchElement.class).attribute("aria-labelledby", id)
+                .first();
path: vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
user: login: vursen
body: It's possible to add a form item without a label by  `formLayout.add(new FormItem(input))`, so I would expect this method to support that use case too:

```suggestion
        return getPropertyElements("children").stream()
                .filter((element) -> element.getDomProperty("validate") != null)
                .findFirst().orElse(null);
```
created_at: 2025-03-27T06:28:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -24,4 +26,30 @@
  */
 @Element("vaadin-form-layout")
 public class FormLayoutElement extends TestBenchElement {
+
+    /**
+     * Retrieves all form items of type {@link FormItemElement}.
+     *
+     * @return a list of all {@link FormItemElement} instances.
+     */
+    public List<FormItemElement> getFormItems() {
+        return $(FormItemElement.class).all();
+    }
+
+    /**
+     * Retrieves a form item element by its label.
+     *
+     * @param label
+     *            the label of the form item to retrieve
+     * @return the form item element with the specified label
+     * @throws IllegalArgumentException
+     *             if no form item is found with the specified label
+     */
+    public FormItemElement getFormItemByLabel(String label) {
+        return getFormItems().stream().filter(item -> {
+            var itemLabel = item.getLabel();
+            return itemLabel != null && itemLabel.getText().equals(label);
+        }).findFirst().orElseThrow(() -> new IllegalArgumentException(
+                "No form item found with label: " + label));
path: vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormLayoutElement.java
user: login: vursen
body: Same question here. What's the benefit of throwing an exception instead of simply returning `null`?

```suggestion
        return getFormItems().stream()
                .filter(item -> label.equals(item.getLabelText())).findFirst()
                .orElse(null);
```
created_at: 2025-03-27T07:13:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.formlayout.testbench;
+
+import java.util.Objects;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a <code>&lt;vaadin-form-item&gt;</code>
+ * element.
+ */
+@Element("vaadin-form-item")
+public class FormItemElement extends TestBenchElement {
+
+    /**
+     * Retrieves the label element from the form items label slot.
+     *
+     * @return the label element as a TestBenchElement
+     */
+    public TestBenchElement getLabel() {
+        return (TestBenchElement) executeScript(
+                "const formItem = arguments[0];"
+                        + "return Array.from(formItem.children).filter("
+                        + "el => el.getAttribute('slot') === 'label'" + ")[0];",
+                this);
path: vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
user: login: TatuLund
body: Both work, but the JavaScript version does only one driver visit, while the Java version can make many.
created_at: 2025-03-27T13:15:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.formlayout.testbench;
+
+import java.util.Objects;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a <code>&lt;vaadin-form-item&gt;</code>
+ * element.
+ */
+@Element("vaadin-form-item")
+public class FormItemElement extends TestBenchElement {
+
+    /**
+     * Retrieves the label element from the form items label slot.
+     *
+     * @return the label element as a TestBenchElement
+     */
+    public TestBenchElement getLabel() {
+        return (TestBenchElement) executeScript(
+                "const formItem = arguments[0];"
+                        + "return Array.from(formItem.children).filter("
+                        + "el => el.getAttribute('slot') === 'label'" + ")[0];",
+                this);
+    }
+
+    /**
+     * Retrieves the text of the label element associated with this form item.
+     *
+     * @return the text of the label element
+     * @throws NullPointerException
+     *             if the label element is not found
+     */
+    public String getLabelText() {
+        var label = getLabel();
+        Objects.requireNonNull(label, "Label element not found");
+        return label.getText();
+    }
+
+    /**
+     * Retrieves the input element associated with the label of this form item.
+     *
+     * @return the input element associated with the label of this form item.
+     */
+    public TestBenchElement getInput() {
+        String id = getLabel().getAttribute("id");
+        return $(TestBenchElement.class).attribute("aria-labelledby", id)
+                .first();
path: vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
user: login: TatuLund
body: Btw, does your suggested version return the Field component? If yes, it could be better option and perhaps the method name should be then updated accordingly?
created_at: 2025-03-27T13:18:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.formlayout.testbench;
+
+import java.util.Objects;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a <code>&lt;vaadin-form-item&gt;</code>
+ * element.
+ */
+@Element("vaadin-form-item")
+public class FormItemElement extends TestBenchElement {
+
+    /**
+     * Retrieves the label element from the form items label slot.
+     *
+     * @return the label element as a TestBenchElement
+     */
+    public TestBenchElement getLabel() {
+        return (TestBenchElement) executeScript(
+                "const formItem = arguments[0];"
+                        + "return Array.from(formItem.children).filter("
+                        + "el => el.getAttribute('slot') === 'label'" + ")[0];",
+                this);
+    }
+
+    /**
+     * Retrieves the text of the label element associated with this form item.
+     *
+     * @return the text of the label element
+     * @throws NullPointerException
+     *             if the label element is not found
+     */
+    public String getLabelText() {
+        var label = getLabel();
+        Objects.requireNonNull(label, "Label element not found");
+        return label.getText();
+    }
+
+    /**
+     * Retrieves the input element associated with the label of this form item.
+     *
+     * @return the input element associated with the label of this form item.
+     */
+    public TestBenchElement getInput() {
+        String id = getLabel().getAttribute("id");
+        return $(TestBenchElement.class).attribute("aria-labelledby", id)
+                .first();
path: vaadin-form-layout-flow-parent/vaadin-form-layout-testbench/src/main/java/com/vaadin/flow/component/formlayout/testbench/FormItemElement.java
user: login: vursen
body: Yes, it returns the Field component, not the input. Sorry, I just forgot to ask – what was the intention behind returning the input directly?

Good point about the name. Should we name it `getField` instead?
created_at: 2025-03-27T13:47:55Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-03-31T08:17:38Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=vaadin_flow-components&pullRequest=7243) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=vaadin_flow-components&pullRequest=7243&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=vaadin_flow-components&pullRequest=7243&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=vaadin_flow-components&pullRequest=7243&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=vaadin_flow-components&pullRequest=7243&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=vaadin_flow-components&pullRequest=7243&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=vaadin_flow-components&pullRequest=7243)


author_association: NONE
user: login: vaadin-bot
created_at: 2025-03-31T10:31:10Z
body: Hi @TatuLund and @vursen, when i performed cherry-pick to this commit to 23.5, i have encountered the following issue. Can you take a look and pick it manually?
 Error Message:
 Error: Command failed: git cherry-pick 002f6ab445fbaa0fc139afb2b4f7db831777b583
error: could not apply 002f6ab445... feat: add FormItemElement and related FormLayoutElement methods (#7243)
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".

author_association: COLLABORATOR