diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/block.json new file mode 100644 index 0000000000000..a6089a224692f --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/block.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "test/directive-ignore", + "title": "E2E Interactivity tests - directive ignore", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScriptModule": "file:./view.js", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/render.php new file mode 100644 index 0000000000000..7593d211d6975 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/render.php @@ -0,0 +1,33 @@ + 0 ) ); +?> + +
'the letter a', + 'b' => 'the letter b', + 'one' => 'the number one', + 'two' => 'the number two', + ) + ); + ?> +> +
+ +
+
No processing should occur here.
+
+ + +
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/view.asset.php b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/view.asset.php new file mode 100644 index 0000000000000..db23afdf657a1 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/view.asset.php @@ -0,0 +1 @@ + array( '@wordpress/interactivity' ) ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/view.js new file mode 100644 index 0000000000000..27d44d9da00b7 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-ignore/view.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { store, getContext } from '@wordpress/interactivity'; + +const { state } = store( 'directive-ignore', { + actions: { + run() { + getContext().one = '1'; + getContext().two = '2'; + }, + click() { + state.clicks += 1; + }, + }, +} ); diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.js index c20ea3aec722d..3f8b61db49732 100644 --- a/packages/interactivity/src/directives.js +++ b/packages/interactivity/src/directives.js @@ -3,7 +3,7 @@ /** * External dependencies */ -import { h as createElement } from 'preact'; +import { h as createElement, Component } from 'preact'; import { useContext, useMemo, useRef } from 'preact/hooks'; import { deepSignal, peek } from 'deepsignal'; @@ -221,6 +221,30 @@ const getGlobalEventDirective = } ); }; +/** + * @augments {Component} + */ +class IgnoredComponent extends Component { + shouldComponentUpdate() { + return false; + } + + render( props ) { + const { + element: { + type: Type, + props: { innerHTML, ...rest }, + }, + } = props; + return ( + + ); + } +} + export default () => { // data-wp-context directive( @@ -440,24 +464,9 @@ export default () => { } ); // data-wp-ignore - directive( - 'ignore', - ( { - element: { - type: Type, - props: { innerHTML, ...rest }, - }, - } ) => { - // Preserve the initial inner HTML. - const cached = useMemo( () => innerHTML, [] ); - return ( - - ); - } - ); + directive( 'ignore', ( args ) => { + return ; + } ); // data-wp-text directive( 'text', ( { directives: { text }, element, evaluate } ) => { diff --git a/packages/interactivity/src/hooks.tsx b/packages/interactivity/src/hooks.tsx index 00c3c0d6d1729..5cb3091bf9558 100644 --- a/packages/interactivity/src/hooks.tsx +++ b/packages/interactivity/src/hooks.tsx @@ -24,7 +24,7 @@ interface DirectiveEntry { type DirectiveEntries = Record< string, DirectiveEntry[] >; -interface DirectiveArgs { +export interface DirectiveArgs { /** * Object map with the defined directives of the element being evaluated. */ @@ -347,7 +347,7 @@ const Directives = ( { ); const props = { ...originalProps, children }; - const directiveArgs = { + const directiveArgs: DirectiveArgs = { directives, props, element, diff --git a/test/e2e/specs/interactivity/directive-each.spec.ts b/test/e2e/specs/interactivity/directive-each.spec.ts index a9f09191685a3..0de9056d91af9 100644 --- a/test/e2e/specs/interactivity/directive-each.spec.ts +++ b/test/e2e/specs/interactivity/directive-each.spec.ts @@ -18,7 +18,7 @@ test.describe( 'data-wp-each', () => { await utils.deleteAllPosts(); } ); - test( 'should use `item` as the defaul item name in the context', async ( { + test( 'should use `item` as the default item name in the context', async ( { page, } ) => { const elements = page.getByTestId( 'letters' ).getByTestId( 'item' ); diff --git a/test/e2e/specs/interactivity/directive-ignore.spec.ts b/test/e2e/specs/interactivity/directive-ignore.spec.ts new file mode 100644 index 0000000000000..2612fc7be040a --- /dev/null +++ b/test/e2e/specs/interactivity/directive-ignore.spec.ts @@ -0,0 +1,47 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-ignore', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-ignore' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-ignore' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'ignored directives should never update the DOM', async ( { + page, + } ) => { + const block = page.getByTestId( 'block' ); + + const ignoredElement = await block + .getByTestId( 'ignored' ) + .evaluate( ( el ) => el ); + const ignoredChildElement = await block + .getByTestId( 'ignored-child' ) + .evaluate( ( el ) => el ); + + await expect( block.getByTestId( 'ignored-child' ) ).toHaveText( + 'No processing should occur here.' + ); + + await expect( block.getByTestId( 'counter' ) ).toHaveText( '0' ); + await block.getByTestId( 'click-me' ).click(); + await expect( block.getByTestId( 'counter' ) ).toHaveText( '1' ); + expect( ignoredElement ).toBe( + await block.getByTestId( 'ignored' ).evaluate( ( el ) => el ) + ); + expect( ignoredChildElement ).toBe( + await block.getByTestId( 'ignored-child' ).evaluate( ( el ) => el ) + ); + } ); +} );