Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f99ced4
Forms: Add Jetpack Form Editor for central form management
enejb Jan 5, 2026
450a20a
changelog
enejb Jan 5, 2026
a773728
Minor fixes
enejb Jan 5, 2026
fe9ae69
Adding php tests
enejb Jan 5, 2026
9c7769f
Minor styles chnges
enejb Jan 5, 2026
0f34bbf
Minor fixes
enejb Jan 5, 2026
ac19ee7
Update to simplify things
enejb Jan 5, 2026
34aa133
Update comment to be more accurate
enejb Jan 5, 2026
97dc16d
Minor
enejb Jan 5, 2026
6955ec8
Add tests
enejb Jan 5, 2026
be905d6
Refactor form editor with pure utilities and add tests
enejb Jan 5, 2026
1ce6287
Remove packages
enejb Jan 5, 2026
af6aba4
fix the test
enejb Jan 5, 2026
c97fc46
Update the shouldLockBlock tests and implementation
enejb Jan 5, 2026
e21b799
fix phan
enejb Jan 5, 2026
117de34
Update projects/packages/forms/src/form-editor/index.tsx
enejb Jan 5, 2026
ebcdb85
Update projects/packages/forms/src/form-editor/class-form-editor.php
enejb Jan 5, 2026
a58e054
Update projects/packages/forms/src/form-editor/use-form-rename-comman…
enejb Jan 5, 2026
aaa2528
Improve the comments
enejb Jan 6, 2026
dddf110
Update the core block list
enejb Jan 6, 2026
94a376a
add column as well
enejb Jan 6, 2026
7592d53
Update to use React.KeyboardEvent
enejb Jan 6, 2026
a5f24aa
update the form title command
enejb Jan 6, 2026
dbd2863
Improve the performance of subscribe
enejb Jan 6, 2026
f62e29d
minor
enejb Jan 6, 2026
543bd41
co pilot fixes
enejb Jan 6, 2026
2801f02
update the lockfiles
enejb Jan 6, 2026
5eefef6
Remove the hidding of the post title and the rename command
enejb Jan 6, 2026
11a412d
Preserve and restore block category order in form editor
enejb Jan 6, 2026
c4e7886
Rename forms editor assets and update block lock typing
enejb Jan 6, 2026
cd58a05
Add version
enejb Jan 7, 2026
7f63ed8
Lets skip the test if the file doesn't exits because the js build scr…
enejb Jan 7, 2026
6b7a364
Update projects/packages/forms/tests/php/form-editor/Form_Editor_Test…
enejb Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions projects/packages/forms/changelog/add-form-editor
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Forms: Add form editor under a feature flag
6 changes: 4 additions & 2 deletions projects/packages/forms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
"author": "Automattic",
"type": "module",
"scripts": {
"build": "pnpm run clean && pnpm run build:blocks && pnpm run build:contact-form && pnpm run build:dashboard && pnpm run module:build",
"build": "pnpm run clean && pnpm run build:blocks && pnpm run build:contact-form && pnpm run build:dashboard && pnpm run build:form-editor && pnpm run module:build",
Copy link
Contributor

@edanzer edanzer Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@enejb - the form editor if feature flagged, but this build code will always run and include the feature flagged form editor. Same with other package.json updates below specific to the form editor.

Is that a concern?

Update: I was also trying find other places the changes might leak out of the feature flag.

  • We bump a dependency version in this file.
  • We've added tests - I assume it's fine if those run during automated testing even if functionality is feature flagged.
  • We update the template for register_post_type in a general file, but that is feature flagged upstream.

Anyways, just worth doing a sanity check that any code outside the feature flag will be safe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing this up. This will definitely create scripts even though the feature flag is not enabled.

I think this is fine since there shouldn't be an impact on the user. (They are not downloading) the script when they visit the editor. (for example) but we might be distributing the assets (js and css) files even though they the site is under the feature flag. ( but the same cases exists for PHP files)

"build-production": "NODE_ENV=production BABEL_ENV=production pnpm run build && pnpm run validate",
"build:blocks": "webpack --config ./tools/webpack.config.blocks.js",
"build:contact-form": "webpack --config ./tools/webpack.config.contact-form.js",
"build:dashboard": "webpack --config ./tools/webpack.config.dashboard.js",
"build:form-editor": "webpack --config ./tools/webpack.config.form-editor.js",
"build:wp-build": "wp-build",
"clean": "rm -rf dist/ .cache/",
"module:build": "webpack --config ./tools/webpack.config.modules.js",
Expand All @@ -29,7 +30,7 @@
"test-coverage": "pnpm run test --coverage",
"typecheck": "tsc --noEmit",
"validate": "pnpm exec validate-es --no-error-on-unmatched-pattern dist/",
"watch": "concurrently 'pnpm:build:blocks --watch' 'pnpm:build:contact-form --watch' 'pnpm:build:dashboard --watch' 'pnpm:module:watch'",
"watch": "concurrently 'pnpm:build:blocks --watch' 'pnpm:build:contact-form --watch' 'pnpm:build:dashboard --watch' 'pnpm:module:watch' 'pnpm:build:form-editor --watch'",
"watch:wp-build": "wp-build --watch"
},
"browserslist": [
Expand Down Expand Up @@ -67,6 +68,7 @@
"@wordpress/interactivity": "6.37.0",
"@wordpress/lazy-editor": "1.3.0",
"@wordpress/notices": "5.37.0",
"@wordpress/plugins": "7.37.0",
"@wordpress/primitives": "4.37.0",
"@wordpress/route": "0.2.0",
"@wordpress/theme": "0.3.0",
Expand Down
6 changes: 6 additions & 0 deletions projects/packages/forms/src/blocks/shared/util/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,9 @@ export const FORM_STYLE = {
DEFAULT: 'default',
OUTLINED: 'outlined',
};

/**
* The custom post type for jetpack forms.
* Matches Contact_Form::POST_TYPE in PHP.
*/
export const FORM_POST_TYPE = 'jetpack_form';
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Constants;
use Automattic\Jetpack\Extensions\Contact_Form\Contact_Form_Block;
use Automattic\Jetpack\Forms\Editor\Form_Editor;
use Automattic\Jetpack\Forms\Jetpack_Forms;
use Automattic\Jetpack\Forms\Service\Form_Webhooks;
use Automattic\Jetpack\Forms\Service\Google_Drive;
Expand Down Expand Up @@ -369,6 +370,7 @@ protected function __construct() {

if ( self::has_editor_feature_flag( 'central-form-management' ) ) {
Contact_Form::register_post_type();
Form_Editor::init();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ public static function register_post_type() {
'map_meta_cap' => true,
'labels' => $labels,
'hierarchical' => false,
'template' => array( array( 'jetpack/contact-form' ) ),
'supports' => array(
'title',
'editor',
Expand Down
158 changes: 158 additions & 0 deletions projects/packages/forms/src/form-editor/class-form-editor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php
/**
* Jetpack forms editor.
*
* @package automattic/jetpack-forms
*/

namespace Automattic\Jetpack\Forms\Editor;

use Automattic\Jetpack\Assets;
use Automattic\Jetpack\Forms\ContactForm\Contact_Form;

/**
* Class Form_Editor
*
* Handles the form editor functionality for jetpack-form post type.
*/
class Form_Editor {

/**
* Script handle for the form editor.
*
* @var string
*/
const SCRIPT_HANDLE = 'jetpack-form-editor';

/**
* Initialize the form editor.
*/
public static function init() {
add_filter( 'allowed_block_types_all', array( __CLASS__, 'allowed_blocks_for_jetpack_form' ), 10, 2 );
add_filter( 'block_editor_settings_all', array( __CLASS__, 'block_editor_settings_all' ), 10, 2 );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin_scripts' ) );
}

/**
* Restrict allowed blocks when editing jetpack-form posts.
*
* Only allows field blocks and supporting blocks. The contact-form block is excluded
* because visual wrapping is handled via DOM manipulation in the editor script.
*
* @param bool|array $allowed_block_types Array of block type slugs, or boolean to enable/disable all.
* @param object $editor_context The current editor context.
* @return bool|array Array of allowed block types for jetpack-form posts.
*/
public static function allowed_blocks_for_jetpack_form( $allowed_block_types, $editor_context ) {
// Only apply to jetpack-form post type.
if ( ! isset( $editor_context->post ) || Contact_Form::POST_TYPE !== $editor_context->post->post_type ) {
return $allowed_block_types;
}

// Allow only field blocks, button, and core blocks.
// Visual wrapping is handled by JavaScript DOM manipulation.
return array(
// Field blocks.
'jetpack/field-name',
'jetpack/field-email',
'jetpack/field-url',
'jetpack/field-telephone',
'jetpack/field-textarea',
'jetpack/field-checkbox',
'jetpack/field-checkbox-multiple',
'jetpack/field-radio',
'jetpack/field-select',
'jetpack/field-date',
'jetpack/field-consent',
'jetpack/field-rating',
'jetpack/field-text',
'jetpack/field-number',
'jetpack/field-file-upload',

// Supporting blocks.
'jetpack/button',
'jetpack/label',
'jetpack/input',
'jetpack/options',
'jetpack/option',
'jetpack/phone-input',

// Multistep blocks.
'jetpack/form-step',
'jetpack/form-step-container',
'jetpack/form-step-divider',
'jetpack/form-step-navigation',
'jetpack/form-progress-indicator',

// Core blocks for rich content.
'core/audio',
'core/columns',
'core/column',
'core/group',
'core/heading',
'core/html',
'core/image',
'core/list',
'core/list-item',
'core/paragraph',
'core/row',
'core/separator',
'core/spacer',
'core/stack',
'core/subhead',
'core/video',
);
Copy link
Contributor

@edanzer edanzer Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need jetpack/contact-form in this list?

}

/**
* Modify block editor settings for jetpack-form posts.
*
* @param array $settings Block editor settings.
* @param object $editor_context The current editor context.
* @return array Modified block editor settings for jetpack-form posts.
*/
public static function block_editor_settings_all( $settings, $editor_context ) {
// Only apply to jetpack-form post type.
if ( ! isset( $editor_context->post ) || Contact_Form::POST_TYPE !== $editor_context->post->post_type ) {
return $settings;
}

// Disable block locking capability.
$settings['canLockBlocks'] = false;

return $settings;
}

/**
* Enqueue admin scripts for block editor.
* Loads in all post block editor contexts (excluding the site editor) so that the
* rename command is available and can be used when a form block is selected.
*/
public static function enqueue_admin_scripts() {
$screen = get_current_screen();

// Only load in block editor contexts, not site editor
if ( ! $screen || $screen->id === 'site-editor' || ! $screen->is_block_editor ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to only be loading this JS when post_type === jetpack_form? I think that's also possible with the $screen var.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my testing when I do that (limit by post type) then the switching in the block editor (clicking the edit form button) doesn't quite work as expected.

But since this feature doesn't load on the site-editor removing the script from the site editor is fine.

return;
}
$asset_file = __DIR__ . '/../../dist/form-editor/jetpack-form-editor.asset.php';
if ( ! file_exists( $asset_file ) ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( 'Form Editor asset file not found: ' . $asset_file );
return;
}
$asset = require $asset_file;
Assets::register_script(
self::SCRIPT_HANDLE,
'../../dist/form-editor/jetpack-form-editor.js',
__FILE__,
array(
'in_footer' => true,
'textdomain' => 'jetpack-forms',
'enqueue' => true,
'dependencies' => $asset['dependencies'],
'version' => $asset['version'],
)
);
}
}
Loading
Loading