diff --git a/admin/metabox/class-metabox.php b/admin/metabox/class-metabox.php index 755005b5c26..abee35f247b 100644 --- a/admin/metabox/class-metabox.php +++ b/admin/metabox/class-metabox.php @@ -361,8 +361,17 @@ protected function render_hidden_fields() { $screen = WP_Screen::get(); $is_block_editor = $screen && $screen->is_block_editor(); if ( $is_block_editor && $this->get_metabox_post()->post_type === 'post' ) { - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class. - echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'content_planner' ); + /** + * Filter: 'wpseo_enable_ai_content_planner_inline_banner' - Allows hiding the AI Content Planner inline banner site-wide. + * + * Returning false stops the hidden meta inputs from being rendered, which the editor JS treats as "banner disabled". + * + * @param bool $enabled Whether the inline banner should be available in the editor. Default true. + */ + if ( apply_filters( 'wpseo_enable_ai_content_planner_inline_banner', true ) ) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class. + echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'content_planner' ); + } } /** diff --git a/packages/js/src/ai-content-planner/helpers/fields.js b/packages/js/src/ai-content-planner/helpers/fields.js index 13f05947b7c..3969038c87f 100644 --- a/packages/js/src/ai-content-planner/helpers/fields.js +++ b/packages/js/src/ai-content-planner/helpers/fields.js @@ -22,10 +22,16 @@ const getInputValue = ( id ) => { }; /** - * Helper functions to get the value of whether the banner is dismissed or rendered. - * @returns {boolean} True if the banner is dismissed or rendered, false otherwise. + * Helper function to get the dismissed state of the inline banner from the post meta. + * + * Returns true when the hidden input is missing, so a server-side filter that stops the input from rendering keeps the banner hidden site-wide. + * + * @returns {boolean} True if the banner is dismissed or its hidden input is absent, false otherwise. */ -export const getIsBannerDismissedFromInput = () => getInputValue( "yoast_wpseo_is_content_planner_banner_dismissed" ) === "1"; +export const getIsBannerDismissedFromInput = () => { + const input = document.getElementById( "yoast_wpseo_is_content_planner_banner_dismissed" ); + return input === null || input.value === "1"; +}; /** * Helper function to get the value of whether the banner is rendered. diff --git a/packages/js/tests/ai-content-planner/helpers/fields.test.js b/packages/js/tests/ai-content-planner/helpers/fields.test.js new file mode 100644 index 00000000000..ce9d26a976c --- /dev/null +++ b/packages/js/tests/ai-content-planner/helpers/fields.test.js @@ -0,0 +1,31 @@ +import { getIsBannerDismissedFromInput } from "../../../src/ai-content-planner/helpers/fields"; + +const INPUT_ID = "yoast_wpseo_is_content_planner_banner_dismissed"; + +const renderInput = ( value ) => { + const input = document.createElement( "input" ); + input.id = INPUT_ID; + input.type = "hidden"; + input.value = value; + document.body.appendChild( input ); +}; + +describe( "getIsBannerDismissedFromInput", () => { + afterEach( () => { + document.body.innerHTML = ""; + } ); + + it( "returns true when the hidden input is absent so a server-side filter can hide the banner", () => { + expect( getIsBannerDismissedFromInput() ).toBe( true ); + } ); + + it( "returns true when the hidden input value is \"1\"", () => { + renderInput( "1" ); + expect( getIsBannerDismissedFromInput() ).toBe( true ); + } ); + + it( "returns false when the hidden input value is \"0\"", () => { + renderInput( "0" ); + expect( getIsBannerDismissedFromInput() ).toBe( false ); + } ); +} );