Skip to content

1122 content planner create an endpoints for inline banner#23105

Merged
JorPV merged 23 commits intofeature/content-plannerfrom
1122-content-planner-create-an-endpoints-for-inline-banner
Apr 7, 2026
Merged

1122 content planner create an endpoints for inline banner#23105
JorPV merged 23 commits intofeature/content-plannerfrom
1122-content-planner-create-an-endpoints-for-inline-banner

Conversation

@vraja-pro
Copy link
Copy Markdown
Contributor

@vraja-pro vraja-pro commented Mar 26, 2026

Context

  • We want to add a WordPress block to allow users to activate the content planner.

Summary

This PR can be summarized in the following changelog entry:

  • Introduces the content planner inline block.

Relevant technical choices:

Test instructions

Test instructions for the acceptance test before the PR gets merged

This PR can be acceptance tested by following these steps:

Setup

  • Have a WordPress install with this branch active.
  • Make sure you are logged in as an editor or admin.

Banner appears on a new post

  1. Go to Posts → Add New to open the block editor for a new post.
  2. Confirm the inline banner appears below the first paragraph block with the text "Stuck on what to write next?".
  3. If testing with a free (non-Premium) account, confirm the banner shows a "Using 1 spark" note. If testing with a Premium account, confirm that note is absent.

Dismiss persists across reloads
4. Click the X button on the banner — confirm the banner disappears.
5. Save or publish the post, then reload the page.
6. Confirm the banner does not reappear after the reload.

Rendered state persists across reloads
7. Create another new post (Posts → Add New) and let the banner render without dismissing it.
8. Save the post as a draft, then reload the page.
9. Confirm the banner is still visible after the reload.

Existing post — no banner
10. Open an existing post that has never had the banner rendered (i.e. was created before this branch was active, or was never opened with the banner).
11. Confirm the banner does not appear.

Non-post post types
12. Go to Pages → Add New to open the block editor for a new page.
13. Confirm the banner does not appear.

Relevant test scenarios

  • Changes should be tested with the browser console open
  • Changes should be tested on different posts/pages/taxonomies/custom post types/custom taxonomies
  • Changes should be tested on different editors (Default Block/Gutenberg/Classic/Elementor/other)
  • Changes should be tested on different browsers
  • Changes should be tested on multisite

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

QA can test this PR by following these steps:

Impact check

This PR affects the following parts of the plugin, which may require extra testing:

  • Open the devtools console and execute:
window.wpseoScriptData.isBlockEditor
  • Check you get '1'

Other environments

  • This PR also affects Shopify. I have added a changelog entry starting with [shopify-seo], added test instructions for Shopify and attached the Shopify label to this PR.
  • This PR also affects Yoast SEO for Google Docs. I have added a changelog entry starting with [yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached the Google Docs Add-on label to this PR.

Documentation

  • I have written documentation for this change. For example, comments in the Relevant technical choices, comments in the code, documentation on Confluence / shared Google Drive / Yoast developer portal, or other.

Quality assurance

  • I have tested this code to the best of my abilities.
  • During testing, I had activated all plugins that Yoast SEO provides integrations for.
  • I have added unit tests to verify the code works as intended.
  • If any part of the code is behind a feature flag, my test instructions also cover cases where the feature flag is switched off.
  • I have written this PR in accordance with my team's definition of done.
  • I have checked that the base branch is correctly set.
  • I have run grunt build:images and commited the results, if my PR introduces new images or SVGs.

Innovation

  • No innovation project is applicable for this PR.
  • This PR falls under an innovation project. I have attached the innovation label.
  • I have added my hours to the WBSO document.

Fixes #

@vraja-pro vraja-pro added the changelog: non-user-facing Needs to be included in the 'Non-userfacing' category in the changelog label Mar 26, 2026
@coveralls
Copy link
Copy Markdown

coveralls commented Mar 26, 2026

Pull Request Test Coverage Report for Build 9ac9fd7eadd0363882d045f0720330a3c32c9b2c

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 3 of 58 (5.17%) changed or added relevant lines in 6 files are covered.
  • 197 unchanged lines in 14 files lost coverage.
  • Overall coverage decreased (-3.2%) to 53.487%

Changes Missing Coverage Covered Lines Changed/Added Lines %
packages/js/src/ai-content-planner/components/inline-banner.js 0 1 0.0%
src/integrations/blocks/content-planner-block.php 0 6 0.0%
src/integrations/blocks/block-categories.php 0 7 0.0%
packages/js/src/ai-content-planner/initialize.js 0 18 0.0%
packages/js/src/ai-content-planner/block.js 0 23 0.0%
Files with Coverage Reduction New Missed Lines %
packages/js/src/components/SidebarCollapsible.js 1 83.33%
packages/js/src/ai-content-planner/components/inline-banner.js 1 0.0%
packages/js/src/ai-content-planner/initialize.js 1 0.0%
packages/js/src/first-time-configuration/tailwind-components/steps/indexation/indexation-step.js 1 16.67%
packages/js/src/first-time-configuration/tailwind-components/steps/finish/finish-step.js 4 0.0%
packages/js/src/components/WebinarPromoNotification.js 4 0.0%
packages/js/src/first-time-configuration/tailwind-components/steps/site-representation/site-representation-step.js 6 1.75%
packages/js/src/general/routes/task-list.js 9 7.41%
packages/js/src/introductions/initialize.js 15 0.0%
packages/js/src/plans/components/cards/ai-plus-card.js 16 0.0%
Totals Coverage Status
Change from base Build a9a036835170bc75394ba87d077461c87aa7e6e1: -3.2%
Covered Lines: 34566
Relevant Lines: 64877

💛 - Coveralls

@vraja-pro vraja-pro force-pushed the 1122-content-planner-create-an-endpoints-for-inline-banner branch from 66b1b1d to efbdd00 Compare March 26, 2026 13:52
@vraja-pro vraja-pro force-pushed the 1122-content-planner-create-an-endpoints-for-inline-banner branch from efbdd00 to b5060fa Compare March 26, 2026 13:59
Copy link
Copy Markdown
Member

@pls78 pls78 left a comment

Choose a reason for hiding this comment

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

Left one tiny observation.

Comment thread admin/metabox/class-metabox.php Outdated
echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'social' );
}

$is_block_editor = WP_Screen::get()->is_block_editor();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see $is_block_editor being redeclared 3 times: is there a specific reason for it not to be a class attribute? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, fixing it

 - Root cause: WP_Screen::get() was called in __construct(), which
  runs during plugins_loaded. WP_Screen isn't loaded until later in
  the WordPress boot sequence.
  - Fix: Removed the direct call from the constructor and instead
  hooked set_is_block_editor() to current_screen, which fires after
  WP_Screen is available. The $is_block_editor property defaults to
  false and gets set before admin_enqueue_scripts fires (where it's
  used).
@vraja-pro vraja-pro force-pushed the 1122-content-planner-create-an-endpoints-for-inline-banner branch from 0798ab9 to b9bfaf1 Compare March 30, 2026 11:00
Copy link
Copy Markdown
Member

@pls78 pls78 left a comment

Choose a reason for hiding this comment

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

Acc: 🚧
There are two things that don't work as expected:

  1. if I create a new post, don't dismiss the banner, save and refresh, the banner disappears
  2. if I create a page I can see the banner

we can't detect in save data if it's coming from block editor, so we check only for post type
@vraja-pro
Copy link
Copy Markdown
Contributor Author

vraja-pro commented Apr 1, 2026

I'm actually rethinking my choices in this PR, following my work on #23117,
Maybe we should make the banner a block and drop the post meta and hidden fields. That way we let the block editor take care of the saving. @pls78

Which means,

  • we detect if it's a new post and then add the banner block after the first paragraph block.
  • When dismissed, we remove that block.
  • No need for post meta, hidden fields and everything I've dne the the class-metabox.php and class-wpseo-meta.php. No need of fields.js.

pls78 and others added 4 commits April 1, 2026 11:58
…ementation

The inline banner state was tracked via hidden input fields, a Redux store
with persistence middleware, and post meta saved through the metabox. All of
this is replaced by a WordPress block approach where the block's presence in
post_content is the state. This commit removes the old mechanism:

- Delete Redux store (banner.js, store/index.js) and persistence middleware
- Delete hidden field helpers (fields.js) and their tests
- Delete HOC container (containers/inline-banner.js)
- Delete ContentPlannerEditorPlugin component and its tests
- Remove content_planner meta field definitions from class-wpseo-meta.php
- Remove content_planner hidden field rendering and saving from class-metabox.php
- Revert is_block_editor from class property back to local variable (trunk pattern)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the HOC filter approach with a proper WordPress block
(yoast/content-planner-banner). The block is auto-inserted after the first
paragraph on new posts and removed from post_content when dismissed.

- Add block.json with inserter:false, multiple:false, reusable:false
- Add block.js with Edit component wrapping InlineBanner and save:null
- Add Content_Planner_Block PHP integration for server-side registration
- Add yoast-ai-blocks category to block-categories.php
- Rewrite initialize.js with auto-insertion logic via editor plugin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the HOC filter approach with a proper WordPress block
(yoast/content-planner-banner). The block is auto-inserted after the first
paragraph on new posts and removed from post_content when dismissed.

- Add block.json with inserter:false, multiple:false, reusable:false
- Add block.js with Edit component wrapping InlineBanner and save:null
- Add Content_Planner_Block PHP integration for server-side registration
- Add yoast-ai-blocks category to block-categories.php
- Rewrite initialize.js with auto-insertion logic via editor plugin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ner' of https://github.com/Yoast/wordpress-seo into 1122-content-planner-create-an-endpoints-for-inline-banner

# Conflicts:
#	src/integrations/blocks/content-planner-block.php
@pls78
Copy link
Copy Markdown
Member

pls78 commented Apr 1, 2026

I'm actually rethinking my choices in this PR, following my work on #23117, Maybe we should make the banner a block and drop the post meta and hidden fields. That way we let the block editor take care of the saving. @pls78

Which means,

* we detect if it's a new post and then add the banner block after the first paragraph block.

* When dismissed, we remove that block.

* No need for post meta, hidden fields and everything I've dne the the class-metabox.php and class-wpseo-meta.php. No need of fields.js.

Done 👍

Copy link
Copy Markdown
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 introduces a new “Content Planner Banner” Gutenberg block and supporting editor logic to automatically place it after the first paragraph on new posts (with a dedicated “Yoast AI Blocks” category).

Changes:

  • Add a PHP integration to register the content planner banner block type and a new block category.
  • Refactor the editor behavior to insert a dedicated banner block (and inject Tailwind styles via the block edit component), removing the previous custom store/HOC approach.
  • Improve SVG icon accessibility via useSvgAria and remove now-obsolete JS tests/store modules.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/integrations/blocks/content-planner-block.php Registers the banner block type on init behind AI_Conditional.
src/integrations/blocks/block-categories.php Adds the yoast-ai-blocks block category.
packages/js/src/ai-content-planner/initialize.js Implements the editor plugin that auto-inserts the banner block on new posts.
packages/js/src/ai-content-planner/block.js Registers the banner block and provides the editor-only Edit UI (with Tailwind CSS injection).
packages/js/src/ai-content-planner/block.json Defines the banner block metadata and support flags.
packages/js/src/ai-content-planner/components/inline-banner.js Adds useSvgAria for icons in the inline banner UI.
packages/js/src/ai-content-planner/components/one-spark-note.js Adds useSvgAria for the spark note icon.
packages/js/src/ai-content-planner/store/index.js Removes the previous custom WP data store.
packages/js/src/ai-content-planner/store/banner.js Removes the previous banner slice/selectors/actions.
packages/js/src/ai-content-planner/content-planner-editor-plugin.js Removes the previous editor plugin implementation tied to the custom store.
packages/js/src/ai-content-planner/containers/inline-banner.js Removes the previous container wiring (store-based dismiss/premium selection).
packages/js/tests/ai-content-planner/content-planner-editor-plugin.test.js Removes the previous unit tests for auto-insertion behavior.
inc/class-wpseo-meta.php Minor formatting/alignment change in meta field definitions.

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

* @return void
*/
public function register_block() {
\register_block_type( \WPSEO_PATH . 'blocks/ai-content-planner/block.json' );
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

register_block_type() points at WPSEO_PATH . 'blocks/ai-content-planner/block.json', but there is no blocks/ directory in the repo and this PR only adds packages/js/src/ai-content-planner/block.json. If the build step doesn’t generate/copy the block.json into blocks/ai-content-planner/, WordPress will fail to register the block at runtime when AI is enabled. Please ensure the build/test setup produces that file at the expected path (and update the test bootstrap required-files list if needed).

Suggested change
\register_block_type( \WPSEO_PATH . 'blocks/ai-content-planner/block.json' );
\register_block_type( \WPSEO_PATH . 'packages/js/src/ai-content-planner/block.json' );

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The blocks directory is created at build time, and block files are copied into it. I just needed to add a stub of the block to let integration tests run.

Comment on lines +17 to +37
function insertBannerAfterFirstParagraph( blocks, insertBlock ) {
const hasBanner = blocks.some( b => b.name === "yoast/content-planner-banner" );
if ( hasBanner ) {
return true;
}

const { isBannerDismissed, shouldShowBanner } = useSelect( select => ( {
isBannerDismissed: select( STORE_NAME )?.getIsBannerDismissed?.(),
shouldShowBanner: select( STORE_NAME )?.getShouldShowBanner?.(),
} ), [] );
// If canvas is empty, insert a paragraph first; the effect will re-run.
if ( blocks.length === 0 ) {
// eslint-disable-next-line no-undefined
insertBlock( createBlock( "core/paragraph" ), 0, undefined, false );
return false;
}

const isNewPost = useSelect( select => select( "core/editor" ).isEditedPostNew(), [] );
const firstParagraphIndex = blocks.findIndex( b => b.name === "core/paragraph" );
if ( firstParagraphIndex === -1 ) {
return false;
}

const { showBanner } = useDispatch( STORE_NAME );
// eslint-disable-next-line no-undefined
insertBlock( createBlock( "yoast/content-planner-banner" ), firstParagraphIndex + 1, undefined, false );
return true;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The auto-insertion behavior for the content planner banner block is no longer covered by unit tests: content-planner-editor-plugin.test.js was removed, but there are no new Jest tests verifying banner insertion (new post + post type conditions, inserting paragraph when empty, avoiding duplicate insertions). Please add/port tests for insertBannerAfterFirstParagraph/ContentPlannerEditorPlugin to prevent regressions.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Valid point 👍

Comment thread packages/js/src/ai-content-planner/components/inline-banner.js Outdated
Copy link
Copy Markdown
Contributor

@JorPV JorPV left a comment

Choose a reason for hiding this comment

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

There is this outline focus on the banner that looks weird. I think because it's a WP block and it doesn fill the whole width, there isn't and easy solution but I would check it with UX or Manuel.

Image

The focus outline doesn't align with the Yoast ui-library focus either.
Image

@JorPV JorPV modified the milestone: feature/content-planner Apr 7, 2026
@JorPV
Copy link
Copy Markdown
Contributor

JorPV commented Apr 7, 2026

LGTM ✅

@JorPV JorPV merged commit 332b7dd into feature/content-planner Apr 7, 2026
51 of 53 checks passed
@JorPV JorPV deleted the 1122-content-planner-create-an-endpoints-for-inline-banner branch April 7, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog: non-user-facing Needs to be included in the 'Non-userfacing' category in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants