Skip to content

Forms: Hide Jetpack block collection from form editor inserter#47390

Open
enejb wants to merge 8 commits intotrunkfrom
fix/hide-jetpack-category-from-form-editor
Open

Forms: Hide Jetpack block collection from form editor inserter#47390
enejb wants to merge 8 commits intotrunkfrom
fix/hide-jetpack-category-from-form-editor

Conversation

@enejb
Copy link
Member

@enejb enejb commented Feb 27, 2026

Before:
Screenshot 2026-02-27 at 12 33 49 PM

After:
Jetpack collection doesn't exist anymore.

Proposed changes:

  • Remove the "Jetpack" block collection from the block inserter when editing forms (jetpack_form post type).
  • The collection is saved on removal and re-registered when navigating away from the form editor, so it remains available in other editing contexts.
  • Form field blocks are already organized into their own granular categories (Basic, Contact Info, Choice, Advanced, Multistep), making the Jetpack collection unnecessary and cluttering in the form editor.

Other information:

  • Have you written new tests for your changes, if applicable?
  • Have you checked the E2E test CI results, and verified that your changes do not break them?
  • Have you tested your changes on WordPress.com, if applicable (if so, you'll see a generated comment below with a script to run)?

Does this pull request change what data or activity we track or use?

No

Testing instructions:

  1. Enable central form management.
add_filter( 'jetpack_block_editor_feature_flags', 'eb_register_feature' );
function eb_register_feature( $flags ) {
    $flags['central-form-management'] = true;
    return $flags;
}
  • Open the page editor.

  • Add a form. Select a variation.

  • Click the Editor form. Notice that Jetpack is not there in the block inserter.

  • Click the back button.
    Notice that Jetpack is in the block inserter.

  • Open a Jetpack Form in the form editor (create or edit a jetpack_form post type).

  • Open the block inserter.

  • Verify that the "Jetpack" collection label no longer appears in the inserter — form blocks should be grouped under their granular form categories (Basic, Contact Info, Choice, etc.) instead.

  • Navigate away from the form editor to a regular post/page editor.

  • Open the block inserter again and verify the "Jetpack" collection is visible as expected.

Changelog

  • Generate changelog entries for this PR (using AI).

🤖 Generated with Claude Code

Remove the "Jetpack" block collection from the block inserter when
editing forms, since form field blocks are organized into their own
granular categories. The collection is saved and restored when leaving
the form editor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 27, 2026 20:28
@enejb enejb added the [Status] Needs Review This PR is ready for review. label Feb 27, 2026
@enejb enejb self-assigned this Feb 27, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the fix/hide-jetpack-category-from-form-editor branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack fix/hide-jetpack-category-from-form-editor

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!

@github-actions github-actions bot added [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. and removed [Status] Needs Review This PR is ready for review. labels Feb 27, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the Jetpack Forms editor experience by hiding the global “Jetpack” block collection in the block inserter when editing jetpack_form posts, while restoring it when leaving the form editor so other editors remain unchanged.

Changes:

  • Added a block-collection utility to remove and later restore the Jetpack block collection via the core/blocks store.
  • Wired the utility into the form editor subscription lifecycle (remove on enter/first setup, restore on leave).
  • Added unit tests for the new utility and a changelog entry.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
projects/packages/forms/src/form-editor/utils/block-collection.ts New utility to remove/restore the Jetpack block collection, storing collection metadata between transitions.
projects/packages/forms/src/form-editor/index.tsx Calls the new remove/restore functions when entering/leaving the form editor.
projects/packages/forms/tests/js/form-editor/utils/block-collection.test.js New Jest tests validating the utility’s basic behavior.
projects/packages/forms/changelog/fix-hide-jetpack-category-from-form-editor Patch changelog entry documenting the inserter change for forms.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 28 to 98
describe( 'removeJetpackBlockCollection', () => {
it( 'should remove the jetpack collection when it exists', () => {
mockGetCollections.mockReturnValue( {
jetpack: { title: 'Jetpack', icon: 'jetpack-icon' },
} );

removeJetpackBlockCollection();

expect( mockRemoveBlockCollection ).toHaveBeenCalledWith( 'jetpack' );
} );

it( 'should not call removeBlockCollection when no jetpack collection exists', () => {
mockGetCollections.mockReturnValue( {} );

removeJetpackBlockCollection();

expect( mockRemoveBlockCollection ).not.toHaveBeenCalled();
} );

it( 'should not call removeBlockCollection when getCollections is unavailable', () => {
mockGetCollections.mockImplementation( () => {
throw new Error( 'not a function' );
} );

// Override select to return an object without getCollections
const dataModule = require( '@wordpress/data' );
const originalSelect = dataModule.select;
dataModule.select = () => ( {} );

removeJetpackBlockCollection();

expect( mockRemoveBlockCollection ).not.toHaveBeenCalled();

dataModule.select = originalSelect;
} );
} );

describe( 'restoreJetpackBlockCollection', () => {
it( 'should restore a previously removed collection', () => {
mockGetCollections.mockReturnValue( {
jetpack: { title: 'Jetpack', icon: 'jetpack-icon' },
} );

removeJetpackBlockCollection();
jest.clearAllMocks();

restoreJetpackBlockCollection();

expect( mockAddBlockCollection ).toHaveBeenCalledWith( 'jetpack', 'Jetpack', 'jetpack-icon' );
} );

it( 'should not call addBlockCollection when no collection was previously saved', () => {
restoreJetpackBlockCollection();

expect( mockAddBlockCollection ).not.toHaveBeenCalled();
} );

it( 'should only restore once after removal', () => {
mockGetCollections.mockReturnValue( {
jetpack: { title: 'Jetpack', icon: 'jetpack-icon' },
} );

removeJetpackBlockCollection();
jest.clearAllMocks();

restoreJetpackBlockCollection();
restoreJetpackBlockCollection();

expect( mockAddBlockCollection ).toHaveBeenCalledTimes( 1 );
} );
} );
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

Tests cover the happy path, but they don’t exercise the defensive branches where dispatch('core/blocks') lacks removeBlockCollection/addBlockCollection. Since the implementation treats those actions as optional, add tests that verify (a) no module state is saved when removal can’t happen, and (b) saved state isn’t discarded when restore can’t run, so the operations remain reversible.

Copilot uses AI. Check for mistakes.
@enejb enejb added [Status] Needs Review This PR is ready for review. and removed [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. labels Feb 27, 2026
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 27, 2026 20:36
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@enejb
Copy link
Member Author

enejb commented Feb 27, 2026

@claude review this pr.

@claude
Copy link

claude bot commented Feb 27, 2026

Claude finished @enejb's task in 1m 47s —— View job


PR Review

  • Gathering context and reading changed files
  • Analyzing code quality and correctness
  • Reviewing tests
  • Writing review feedback

Overview

This is a clean, well-structured PR that follows existing patterns in the codebase. The approach of removing the Jetpack block collection on form editor entry and restoring it on exit mirrors the existing disableBlockDirectory / restoreBlockDirectory pattern in index.tsx, which is a good consistency win.

block-collection.ts — Implementation

Good news for Copilot's concerns: Both issues flagged by Copilot in their review have already been addressed in the current code (likely fixed in the two follow-up commits c7f28f31 and 14cb37f6):

  • savedCollection is only set after the removeBlockCollection existence check (line 52 comes after the guard at lines 48–50) ✅
  • savedCollection = null is only called after a successful addBlockCollection call (lines 75–80), not when the API is unavailable ✅

Minor observation — icon?: unknown type: The icon field on BlockCollectionData is typed as unknown. This is acceptable and safe, but if Gutenberg's block collection API exposes a type for icons (e.g. from @wordpress/components or @wordpress/blocks), aligning with that would improve type safety.

index.tsx — Integration

The wiring looks correct:

  • removeJetpackBlockCollection() is called inside the ! state.categoriesSetUp guard (line 415), so it runs exactly once when entering the form editor.
  • restoreJetpackBlockCollection() is called in the "leave" branch (line 389), consistent with restoreBlockDirectory() and restoreAllowedBlocks().

No concerns here.

Tests — Coverage Gaps

The main gap identified by Copilot (comment 3) is legitimate: tests don't exercise the defensive branches where dispatch actions are missing.

Current test at lines 47–62 covers missing getCollections on select, but there are no tests for:

  1. removeJetpackBlockCollection when removeBlockCollection is absent from dispatch — should verify savedCollection is NOT populated (so restore is truly a no-op).
  2. restoreJetpackBlockCollection when addBlockCollection is absent from dispatch — should verify savedCollection is NOT cleared (so a later restore can still succeed).

These defensive branches are important because they're the whole point of the typeof guards. Here's what those tests could look like:

it( 'should not save state when removeBlockCollection is unavailable', () => {
    mockGetCollections.mockReturnValue( {
        jetpack: { title: 'Jetpack', icon: 'jetpack-icon' },
    } );

    // Override dispatch to return object without removeBlockCollection
    const dataModule = require( '@wordpress/data' );
    const originalDispatch = dataModule.dispatch;
    dataModule.dispatch = () => ( {} );

    removeJetpackBlockCollection();

    dataModule.dispatch = originalDispatch;

    // Now restore should be a no-op since nothing was actually removed
    restoreJetpackBlockCollection();
    expect( mockAddBlockCollection ).not.toHaveBeenCalled();
} );

it( 'should preserve savedCollection when addBlockCollection is unavailable', () => {
    mockGetCollections.mockReturnValue( {
        jetpack: { title: 'Jetpack', icon: 'jetpack-icon' },
    } );
    removeJetpackBlockCollection();
    jest.clearAllMocks();

    // Override dispatch to return object without addBlockCollection
    const dataModule = require( '@wordpress/data' );
    const originalDispatch = dataModule.dispatch;
    dataModule.dispatch = () => ( {} );

    restoreJetpackBlockCollection(); // Should be a no-op, NOT clear savedCollection

    dataModule.dispatch = originalDispatch;

    // Restore should still work now that the API is available
    restoreJetpackBlockCollection();
    expect( mockAddBlockCollection ).toHaveBeenCalledWith( 'jetpack', 'Jetpack', 'jetpack-icon' );
} );

Fix this →

Minor: beforeEach calls jest.clearAllMocks() twice

beforeEach( () => {
    jest.clearAllMocks();               // First clear
    restoreJetpackBlockCollection();    // Reset module state
    jest.clearAllMocks();               // Second clear (redundant with first)
} );

The first jest.clearAllMocks() before restoreJetpackBlockCollection() is redundant — you could just call restoreJetpackBlockCollection() first and then jest.clearAllMocks() once. Not a blocker, just a nit.

Changelog

Correct: patch significance and changed type for a UX-only change with no public API impact. ✅

Summary

Area Status
Core implementation logic ✅ Correct — Copilot's two concerns are already addressed
Integration in index.tsx ✅ Looks good
Test: happy-path coverage ✅ Good
Test: defensive branch coverage ⚠️ Missing — worth adding
Changelog ✅ Appropriate

The implementation is solid. The only meaningful gap is the test coverage for the defensive dispatch-API-missing branches. If you want to ship without those tests, the risk is low (the production code is correct), but it would leave those guards unverified.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jp-launch-control
Copy link

jp-launch-control bot commented Feb 27, 2026

Code Coverage Summary

Coverage changed in 2 files.

File Coverage Δ% Δ Uncovered
projects/packages/forms/src/form-editor/class-form-editor.php 73/98 (74.49%) -2.93% 4 💔
projects/packages/forms/src/form-editor/index.tsx 0/199 (0.00%) 0.00% 2 ❤️‍🩹

1 file is newly checked for coverage.

File Coverage
projects/packages/forms/src/form-editor/utils/block-collection.ts 24/24 (100.00%) 💚

Full summary · PHP report · JS report

If appropriate, add one of these labels to override the failing coverage check: Covered by non-unit tests Use to ignore the Code coverage requirement check when E2Es or other non-unit tests cover the code Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR I don't care about code coverage for this PR Use this label to ignore the check for insufficient code coveage.

enejb and others added 2 commits February 27, 2026 12:56
Replace the require()-based mock override with mockSelect.mockReturnValue()
to be compatible with the ESM test environment used in CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use `await jest.unstable_mockModule()` and dynamic `await import()`
instead of `jest.mock()` which relies on `require()` unavailable in
the ESM test environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 27, 2026 21:12
@simison
Copy link
Member

simison commented Feb 27, 2026

Would it altenratively work to check if we're at form custom post type editor and not register in first place, or use Jetpack_Editor_Initial_State?.registerBlockCollection to avoid registering it? Or do we necessarily need the de-registration/re-registration dance?

if ( window?.Jetpack_Editor_Initial_State?.registerBlockCollection ) {

Add tests verifying that:
- removeJetpackBlockCollection does not save state when
  removeBlockCollection is missing from dispatch
- restoreJetpackBlockCollection preserves savedCollection when
  addBlockCollection is missing from dispatch, allowing a later
  restore to succeed

Also clean up redundant jest.clearAllMocks() in beforeEach by
extracting a shared resetMocks helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


describe( 'block-collection', () => {
beforeEach( () => {
resetMocks();
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The beforeEach has duplicated jest.clearAllMocks() and repeated mockSelect.mockImplementation(...) calls. This makes the setup harder to follow and isn't needed to reset the mocks/module state; consider keeping a single clear + a single mockSelect setup, then calling restoreJetpackBlockCollection() (if desired) between them.

Suggested change
resetMocks();

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 27, 2026 21:19
@enejb
Copy link
Member Author

enejb commented Feb 27, 2026

Would it altenratively work to check if we're at form custom post type editor and not register in first place, or use Jetpack_Editor_Initial_State?.registerBlockCollection to avoid registering it? Or do we necessarily need the de-registration/re-registration dance?

Good call. I didn't know this existed.
We will still need to do this dance when the user is coming from the post/page editor. But when we send the user to the form editor directly then we should be able to skip this.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

disableBlockDirectory();
removeJetpackBlockCollection();
}

Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

removeJetpackBlockCollection() is only invoked inside the one-time categoriesSetUp block. If the Jetpack block collection is registered later in the load sequence (e.g., by projects/plugins/jetpack/extensions/editor.js importing ./shared/block-category), the initial removal can be a no-op and the collection may still appear in the inserter for jetpack_form. Consider re-attempting collection removal while isFormEditor is true until it actually succeeds (or tracking a separate collectionRemoved flag rather than piggybacking on categoriesSetUp).

Suggested change
// Ensure the Jetpack block collection is removed even if it is
// registered later in the load sequence.
removeJetpackBlockCollection();

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants