diff --git a/projects/packages/forms/changelog/update-forms-text-field-height b/projects/packages/forms/changelog/update-forms-text-field-height
new file mode 100644
index 0000000000000..8cae830b15772
--- /dev/null
+++ b/projects/packages/forms/changelog/update-forms-text-field-height
@@ -0,0 +1,4 @@
+Significance: minor
+Type: added
+
+Forms: allow height control for textarea"
diff --git a/projects/packages/forms/src/blocks/contact-form/child-blocks.js b/projects/packages/forms/src/blocks/contact-form/child-blocks.js
index ef581409aeb10..7b3da6caca90c 100644
--- a/projects/packages/forms/src/blocks/contact-form/child-blocks.js
+++ b/projects/packages/forms/src/blocks/contact-form/child-blocks.js
@@ -28,6 +28,7 @@ import JetpackStepContainer from '../form-step-container/index.js';
import JetpackStepDivider from '../form-step-divider/index.js';
import JetpackStepNavigation from '../form-step-navigation/index.js';
import JetpackInput from '../input/index.js';
+import JetpackTextarea from '../textarea/index.js';
import JetpackImageOptionInput from '../input-image-option/index.tsx';
import JetpackPhoneInput from '../input-phone/index.js';
import JetpackSliderInput from '../input-range/index.js';
@@ -40,6 +41,7 @@ export const childBlocks = [
JetpackLabel,
JetpackDropzone,
JetpackInput,
+ JetpackTextarea,
JetpackOption,
JetpackOptions,
JetpackCheckboxField,
diff --git a/projects/packages/forms/src/blocks/contact-form/class-contact-form-block.php b/projects/packages/forms/src/blocks/contact-form/class-contact-form-block.php
index 81b90d613a8f4..1941a27bcbc7b 100644
--- a/projects/packages/forms/src/blocks/contact-form/class-contact-form-block.php
+++ b/projects/packages/forms/src/blocks/contact-form/class-contact-form-block.php
@@ -146,6 +146,45 @@ public static function register_child_blocks() {
'uses_context' => array( 'jetpack/field-default-value' ),
)
);
+
+ // Field inner block types.
+ Blocks::jetpack_register_block(
+ 'jetpack/textarea',
+ array(
+ 'supports' => array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'radius' => true,
+ 'style' => true,
+ 'width' => true,
+ ),
+ 'color' => array(
+ 'text' => true,
+ 'background' => true,
+ 'gradients' => false,
+ ),
+ 'dimensions' => array(
+ 'minHeight' => true,
+ ),
+ 'typography' => array(
+ 'fontSize' => true,
+ 'lineHeight' => true,
+ '__experimentalFontFamily' => true,
+ '__experimentalFontWeight' => true,
+ '__experimentalFontStyle' => true,
+ '__experimentalTextTransform' => true,
+ '__experimentalTextDecoration' => true,
+ '__experimentalLetterSpacing' => true,
+ ),
+ ),
+ 'selectors' => array(
+ 'border' => '.wp-block-jetpack-input, .is-style-outlined .notched-label:has(+ .wp-block-jetpack-input) > *,.is-style-outlined .wp-block-jetpack-input + .notched-label > *, .is-style-outlined .wp-block-jetpack-field-select .notched-label > *',
+ 'color' => '.wp-block-jetpack-input, .is-style-outlined .notched-label:has(+ .wp-block-jetpack-input) > *,.is-style-outlined .wp-block-jetpack-input + .notched-label > *, .is-style-outlined .wp-block-jetpack-field-select .notched-label > *',
+ ),
+ 'uses_context' => array( 'jetpack/field-default-value' ),
+ )
+ );
+
Blocks::jetpack_register_block(
'jetpack/label',
array(
diff --git a/projects/packages/forms/src/blocks/contact-form/variations.js b/projects/packages/forms/src/blocks/contact-form/variations.js
index ee870c0bfcaf1..12c4129c9b9e2 100644
--- a/projects/packages/forms/src/blocks/contact-form/variations.js
+++ b/projects/packages/forms/src/blocks/contact-form/variations.js
@@ -62,7 +62,7 @@ const variations = [
{},
[
[ 'jetpack/label', { label: __( 'Message', 'jetpack-forms' ) } ],
- [ 'jetpack/input', { type: 'textarea' } ],
+ [ 'jetpack/textarea' ],
],
],
[
@@ -141,7 +141,7 @@ const variations = [
{},
[
[ 'jetpack/label', { label: __( 'Other details', 'jetpack-forms' ) } ],
- [ 'jetpack/input', { type: 'textarea' } ],
+ [ 'jetpack/textarea' ],
],
],
[
@@ -208,7 +208,7 @@ const variations = [
name: 'jetpack/label',
attributes: { label: __( 'Other details', 'jetpack-forms' ) },
},
- { name: 'jetpack/input', attributes: { type: 'textarea' } },
+ { name: 'jetpack/textarea' },
],
},
{
@@ -294,7 +294,7 @@ const variations = [
label: __( 'Other details', 'jetpack-forms' ),
},
],
- [ 'jetpack/input', { type: 'textarea' } ],
+ [ 'jetpack/textarea' ],
],
],
[
@@ -481,10 +481,7 @@ const variations = [
[
'jetpack/field-textarea',
{},
- [
- [ 'jetpack/label', { label: __( 'Notes', 'jetpack-forms' ) } ],
- [ 'jetpack/input', { type: 'textarea' } ],
- ],
+ [ [ 'jetpack/label', { label: __( 'Notes', 'jetpack-forms' ) } ], [ 'jetpack/textarea' ] ],
],
[
'jetpack/button',
@@ -629,7 +626,7 @@ const variations = [
{},
[
[ 'jetpack/label', { label: __( 'How could we improve?', 'jetpack-forms' ) } ],
- [ 'jetpack/input', { type: 'textarea' } ],
+ [ 'jetpack/textarea' ],
],
],
[
@@ -803,7 +800,7 @@ const variations = [
'jetpack/label',
{ label: __( 'What do you need help with?', 'jetpack-forms' ) },
],
- [ 'jetpack/input', { type: 'textarea' } ],
+ [ 'jetpack/textarea' ],
],
],
[
diff --git a/projects/packages/forms/src/blocks/field-textarea/edit.js b/projects/packages/forms/src/blocks/field-textarea/edit.js
index 90b0a13bbe26e..c4151d14a67f0 100644
--- a/projects/packages/forms/src/blocks/field-textarea/edit.js
+++ b/projects/packages/forms/src/blocks/field-textarea/edit.js
@@ -27,7 +27,7 @@ export default function TextareaFieldEdit( props ) {
const template = useMemo( () => {
return [
[ 'jetpack/label', { label: __( 'Message', 'jetpack-forms' ), requiredIndicator } ],
- [ 'jetpack/input', { type: 'textarea' } ],
+ [ 'jetpack/textarea' ],
];
}, [ requiredIndicator ] );
diff --git a/projects/packages/forms/src/blocks/field-textarea/index.js b/projects/packages/forms/src/blocks/field-textarea/index.js
index b8befb5461fa3..f0f68e5145cea 100644
--- a/projects/packages/forms/src/blocks/field-textarea/index.js
+++ b/projects/packages/forms/src/blocks/field-textarea/index.js
@@ -33,10 +33,7 @@ const settings = {
},
},
{
- name: 'jetpack/input',
- attributes: {
- type: 'textarea',
- },
+ name: 'jetpack/textarea',
},
],
},
diff --git a/projects/packages/forms/src/blocks/textarea/edit.js b/projects/packages/forms/src/blocks/textarea/edit.js
new file mode 100644
index 0000000000000..5a8eb3ee07249
--- /dev/null
+++ b/projects/packages/forms/src/blocks/textarea/edit.js
@@ -0,0 +1,109 @@
+import { store as blockEditorStore, useBlockProps } from '@wordpress/block-editor';
+import { ResizableBox } from '@wordpress/components';
+import { useSelect } from '@wordpress/data';
+import { useCallback } from '@wordpress/element';
+import { useSyncedAttributes } from '../shared/hooks/use-synced-attributes.js';
+import useVariationStyleProperties from '../shared/hooks/use-variation-style-properties.js';
+
+const SYNCED_ATTRIBUTE_KEYS = [
+ 'backgroundColor',
+ 'borderColor',
+ 'fontFamily',
+ 'fontSize',
+ 'style',
+ 'textColor',
+];
+
+const TextareaEdit = ( {
+ attributes,
+ clientId,
+ context,
+ isSelected,
+ name,
+ setAttributes,
+ toggleSelection,
+} ) => {
+ const { 'jetpack/field-share-attributes': isSynced } = context;
+ useSyncedAttributes( name, isSynced, SYNCED_ATTRIBUTE_KEYS, attributes, setAttributes );
+ const { placeholder, style } = attributes;
+
+ // Get height from style.dimensions.minHeight (global styles) or fallback to height attribute
+ const minHeightFromStyle = style?.dimensions?.minHeight;
+ const currentHeight = minHeightFromStyle
+ ? parseInt( minHeightFromStyle.replace( 'px', '' ), 10 ) || 200
+ : attributes.height || 200;
+
+ // Check if the parent block (e.g. textarea-field) is selected
+ const isParentSelected = useSelect(
+ select => {
+ const { getBlockRootClientId, getSelectedBlockClientId } = select( blockEditorStore );
+ const parentClientId = getBlockRootClientId( clientId );
+ const selectedBlockClientId = getSelectedBlockClientId();
+ return parentClientId && parentClientId === selectedBlockClientId;
+ },
+ [ clientId ]
+ );
+
+ const variationProps = useVariationStyleProperties( {
+ clientId,
+ inputBlockName: name,
+ inputBlockAttributes: attributes,
+ } );
+ const blockProps = useBlockProps( {
+ className: 'jetpack-field__textarea',
+ style: variationProps?.cssVars,
+ } );
+
+ const onChange = useCallback(
+ event => {
+ setAttributes( { placeholder: event.target.value } );
+ },
+ [ setAttributes ]
+ );
+
+ return (
+ {
+ const newHeight = currentHeight + delta.height;
+ setAttributes( {
+ style: {
+ ...style,
+ dimensions: {
+ ...style?.dimensions,
+ minHeight: `${ newHeight }px`,
+ },
+ },
+ } );
+ toggleSelection( true );
+ } }
+ onResizeStart={ () => {
+ toggleSelection( false );
+ } }
+ showHandle={ isSelected || isParentSelected }
+ >
+
+
+ );
+};
+
+export default TextareaEdit;
diff --git a/projects/packages/forms/src/blocks/textarea/index.js b/projects/packages/forms/src/blocks/textarea/index.js
new file mode 100644
index 0000000000000..07cd562ca9ad8
--- /dev/null
+++ b/projects/packages/forms/src/blocks/textarea/index.js
@@ -0,0 +1,70 @@
+import { Path } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+import renderMaterialIcon from '../shared/components/render-material-icon.js';
+import edit from './edit.js';
+import save from './save.js';
+
+const name = 'textarea';
+const settings = {
+ apiVersion: 3,
+ title: __( 'Textarea', 'jetpack-forms' ),
+ description: __( 'A textarea for a form field', 'jetpack-forms' ),
+ category: 'contact-form',
+ icon: {
+ src: renderMaterialIcon(
+
+ ),
+ },
+ parent: [ 'jetpack/field-textarea' ],
+ usesContext: [ 'jetpack/field-share-attributes' ],
+ supports: {
+ reusable: false,
+ html: false,
+ color: {
+ text: true,
+ background: true,
+ gradients: false,
+ },
+ __experimentalBorder: {
+ color: true,
+ radius: true,
+ style: true,
+ width: true,
+ __experimentalDefaultControls: {
+ color: true,
+ radius: true,
+ style: true,
+ width: true,
+ },
+ },
+ dimensions: {
+ minHeight: true,
+ },
+ typography: {
+ fontSize: true,
+ lineHeight: true,
+ __experimentalFontFamily: true,
+ __experimentalFontWeight: true,
+ __experimentalFontStyle: true,
+ __experimentalTextTransform: true,
+ __experimentalTextDecoration: true,
+ __experimentalLetterSpacing: true,
+ __experimentalDefaultControls: {
+ fontSize: true,
+ },
+ },
+ },
+ attributes: {
+ placeholder: {
+ type: 'string',
+ default: '',
+ },
+ },
+ edit,
+ save,
+};
+
+export default {
+ name,
+ settings,
+};
diff --git a/projects/packages/forms/src/blocks/textarea/save.js b/projects/packages/forms/src/blocks/textarea/save.js
new file mode 100644
index 0000000000000..461f67a0a4bcb
--- /dev/null
+++ b/projects/packages/forms/src/blocks/textarea/save.js
@@ -0,0 +1 @@
+export default () => null;
diff --git a/projects/packages/forms/src/contact-form/class-contact-form-field.php b/projects/packages/forms/src/contact-form/class-contact-form-field.php
index 6fce4b93dbe8d..4622bb7018863 100644
--- a/projects/packages/forms/src/contact-form/class-contact-form-field.php
+++ b/projects/packages/forms/src/contact-form/class-contact-form-field.php
@@ -1209,10 +1209,9 @@ public function render_textarea_field( $id, $label, $value, $class, $required, $
$field = $this->render_label( 'textarea', 'contact-form-comment-' . $id, $label, $required, $required_field_text, array(), false, $required_indicator );
$aria_label = ! empty( $placeholder ) ? $placeholder : Contact_Form_Plugin::strip_tags( $this->get_attribute( 'label' ) );
$field .= "