From 276314b79ee3c25f6b922ee7822c72945b6c8730 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Wed, 17 Jan 2024 18:20:55 +0100 Subject: [PATCH 01/11] Add wp-data-on-window and wp-data-on-document directives --- .../directive-on-document/block.json | 15 ++++++++++ .../directive-on-document/render.php | 15 ++++++++++ .../directive-on-document/view.js | 16 ++++++++++ .../directive-on-window/block.json | 15 ++++++++++ .../directive-on-window/render.php | 15 ++++++++++ .../directive-on-window/view.js | 16 ++++++++++ packages/interactivity/src/directives.js | 23 ++++++++++++++- .../directive-on-document.spec.ts | 29 +++++++++++++++++++ .../interactivity/directive-on-window.spec.ts | 28 ++++++++++++++++++ 9 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-on-document/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-on-window/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js create mode 100644 test/e2e/specs/interactivity/directive-on-document.spec.ts create mode 100644 test/e2e/specs/interactivity/directive-on-window.spec.ts diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/block.json new file mode 100644 index 0000000000000..296a9b2f04190 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/block.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "test/directive-on-document", + "title": "E2E Interactivity tests - directive on document", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-on-document-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php new file mode 100644 index 0000000000000..2ba7ce0ab33e1 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php @@ -0,0 +1,15 @@ + + +
+
+

0

+
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js new file mode 100644 index 0000000000000..71ae0a0a5d474 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { store } from '@wordpress/interactivity'; + +const { state } = store( 'directive-on-document', { + state: { + counter: 0, + }, + actions: { + counter: 0, + keydownHandler: ( ) => { + state.counter += 1; + }, + }, +} ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/block.json new file mode 100644 index 0000000000000..9d55395b087a6 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/block.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "test/directive-on-window", + "title": "E2E Interactivity tests - directive on window", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-on-window-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php new file mode 100644 index 0000000000000..9c1171ab5a53c --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php @@ -0,0 +1,15 @@ + + +
+
+

0

+
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js new file mode 100644 index 0000000000000..c46a1030f3e35 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { store } from '@wordpress/interactivity'; + +const { state } = store( 'directive-on-window', { + state: { + counter: false, + }, + actions: { + counter: 0, + resizeHandler: ( ) => { + state.counter += 1; + }, + }, +} ); diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.js index c24c01ecdfa63..9ab51b0ba9bdb 100644 --- a/packages/interactivity/src/directives.js +++ b/packages/interactivity/src/directives.js @@ -8,7 +8,7 @@ import { deepSignal, peek } from 'deepsignal'; * Internal dependencies */ import { createPortal } from './portals'; -import { useWatch, useInit } from './utils'; +import { useWatch, useInit, useEffect } from './utils'; import { directive } from './hooks'; const isObject = ( item ) => @@ -87,6 +87,27 @@ export default () => { } ); } ); + const getGlobalEventDirective = + ( type ) => + ( { directives, evaluate } ) => { + directives[ `on-${ type }` ] + .filter( ( { suffix } ) => suffix !== 'default' ) + .forEach( ( entry ) => { + useEffect( () => { + const cb = ( event ) => evaluate( entry, event ); + const globalVar = type === 'window' ? window : document; + globalVar.addEventListener( entry.suffix, cb ); + return () => + globalVar.removeEventListener( entry.suffix, cb ); + }, [] ); + } ); + }; + + // data-wp-on-window--[event] + directive( 'on-window', getGlobalEventDirective( 'window' ) ); + // data-wp-on-document--[event] + directive( 'on-document', getGlobalEventDirective( 'document' ) ); + // data-wp-class--[classname] directive( 'class', diff --git a/test/e2e/specs/interactivity/directive-on-document.spec.ts b/test/e2e/specs/interactivity/directive-on-document.spec.ts new file mode 100644 index 0000000000000..e7333a9841938 --- /dev/null +++ b/test/e2e/specs/interactivity/directive-on-document.spec.ts @@ -0,0 +1,29 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-on-document', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-on-document' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-on-document' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'callbacks should run whenever the specified event is dispatched', async ( { + page, + } ) => { + const counter = page.getByTestId( 'counter' ); + await expect( counter ).toHaveText( '0' ); + await page.keyboard.press( 'ArrowDown' ); + await expect( counter ).toHaveText( '1' ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directive-on-window.spec.ts b/test/e2e/specs/interactivity/directive-on-window.spec.ts new file mode 100644 index 0000000000000..991a44586c7e1 --- /dev/null +++ b/test/e2e/specs/interactivity/directive-on-window.spec.ts @@ -0,0 +1,28 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-on-window', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-on-window' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-on-window' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'callbacks should run whenever the specified event is dispatched', async ( { + page, + } ) => { + await page.setViewportSize( { width: 600, height: 600 } ); + const counter = page.getByTestId( 'counter' ); + await expect( counter ).toHaveText( '1' ); + } ); +} ); From 7fe048879ff97a609cfa32e6561dc2a1948f9d80 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Wed, 17 Jan 2024 18:30:56 +0100 Subject: [PATCH 02/11] Update changelog --- packages/interactivity/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index 8514ecc6716f8..8a09c2d856416 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -9,6 +9,7 @@ ### New Features - Add the `data-wp-run` directive along with the `useInit` and `useWatch` hooks. ([57805](https://github.com/WordPress/gutenberg/pull/57805)) +- Add `wp-data-on-window` and `wp-data-on-document` directives. ([57931](https://github.com/WordPress/gutenberg/pull/57931)) ### Enhancements From c0bd9a2fb63835ef936b6b5788dff36d40ffde6d Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Wed, 17 Jan 2024 18:31:15 +0100 Subject: [PATCH 03/11] Rename directives tests files to unify them --- .../{directives-body.spec.ts => directive-body.spec.ts} | 0 .../{directives-class.spec.ts => directive-class.spec.ts} | 0 .../{directives-context.spec.ts => directive-context.spec.ts} | 0 .../{directives-style.spec.ts => directive-style.spec.ts} | 0 .../{directives-text.spec.ts => directive-text.spec.ts} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename test/e2e/specs/interactivity/{directives-body.spec.ts => directive-body.spec.ts} (100%) rename test/e2e/specs/interactivity/{directives-class.spec.ts => directive-class.spec.ts} (100%) rename test/e2e/specs/interactivity/{directives-context.spec.ts => directive-context.spec.ts} (100%) rename test/e2e/specs/interactivity/{directives-style.spec.ts => directive-style.spec.ts} (100%) rename test/e2e/specs/interactivity/{directives-text.spec.ts => directive-text.spec.ts} (100%) diff --git a/test/e2e/specs/interactivity/directives-body.spec.ts b/test/e2e/specs/interactivity/directive-body.spec.ts similarity index 100% rename from test/e2e/specs/interactivity/directives-body.spec.ts rename to test/e2e/specs/interactivity/directive-body.spec.ts diff --git a/test/e2e/specs/interactivity/directives-class.spec.ts b/test/e2e/specs/interactivity/directive-class.spec.ts similarity index 100% rename from test/e2e/specs/interactivity/directives-class.spec.ts rename to test/e2e/specs/interactivity/directive-class.spec.ts diff --git a/test/e2e/specs/interactivity/directives-context.spec.ts b/test/e2e/specs/interactivity/directive-context.spec.ts similarity index 100% rename from test/e2e/specs/interactivity/directives-context.spec.ts rename to test/e2e/specs/interactivity/directive-context.spec.ts diff --git a/test/e2e/specs/interactivity/directives-style.spec.ts b/test/e2e/specs/interactivity/directive-style.spec.ts similarity index 100% rename from test/e2e/specs/interactivity/directives-style.spec.ts rename to test/e2e/specs/interactivity/directive-style.spec.ts diff --git a/test/e2e/specs/interactivity/directives-text.spec.ts b/test/e2e/specs/interactivity/directive-text.spec.ts similarity index 100% rename from test/e2e/specs/interactivity/directives-text.spec.ts rename to test/e2e/specs/interactivity/directive-text.spec.ts From 790587ed9be0864009c12dd7aa7e9c78c07a8b13 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Wed, 17 Jan 2024 21:36:42 +0100 Subject: [PATCH 04/11] Use callbacks instead of actions, remove not needed counter --- .../interactive-blocks/directive-on-document/render.php | 2 +- .../plugins/interactive-blocks/directive-on-document/view.js | 3 +-- .../interactive-blocks/directive-on-window/render.php | 2 +- .../plugins/interactive-blocks/directive-on-window/view.js | 5 ++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php index 2ba7ce0ab33e1..8fdaed8907712 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php @@ -9,7 +9,7 @@ ?>
-
+

0

diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js index 71ae0a0a5d474..3a170f54489a0 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js @@ -7,8 +7,7 @@ const { state } = store( 'directive-on-document', { state: { counter: 0, }, - actions: { - counter: 0, + callbacks: { keydownHandler: ( ) => { state.counter += 1; }, diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php index 9c1171ab5a53c..3a7fcc5e8e9e6 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php @@ -9,7 +9,7 @@ ?>
-
+

0

diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js index c46a1030f3e35..1fc1e938972de 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js @@ -5,10 +5,9 @@ import { store } from '@wordpress/interactivity'; const { state } = store( 'directive-on-window', { state: { - counter: false, - }, - actions: { counter: 0, + }, + callbacks: { resizeHandler: ( ) => { state.counter += 1; }, From 99e2ed82f985caff54a8a09745b91fca39241464 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Thu, 18 Jan 2024 12:29:57 +0100 Subject: [PATCH 05/11] Add event listener removal tests --- .../directive-on-document/render.php | 9 +- .../directive-on-document/view.js | 21 +++- .../directive-on-window/render.php | 9 +- .../directive-on-window/view.js | 21 +++- packages/interactivity/src/directives.js | 111 ++++++++++-------- .../directive-on-document.spec.ts | 16 +++ .../interactivity/directive-on-window.spec.ts | 15 +++ 7 files changed, 142 insertions(+), 60 deletions(-) diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php index 8fdaed8907712..d04cb79ea5852 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/render.php @@ -8,8 +8,11 @@ gutenberg_enqueue_module( 'directive-on-document-view' ); ?> -
-
-

0

+
+ +
+
+

0

+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js index 3a170f54489a0..b3cb891e4d6cb 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-document/view.js @@ -1,7 +1,20 @@ /** * WordPress dependencies */ -import { store } from '@wordpress/interactivity'; +import { store, directive, getContext } from '@wordpress/interactivity'; + +// Mock `data-wp-show` directive to test when things are removed from the +// DOM. Replace with `data-wp-show` when it's ready. +directive( + 'show-mock', + ( { directives: { 'show-mock': showMock }, element, evaluate } ) => { + const entry = showMock.find( ( { suffix } ) => suffix === 'default' ); + if ( ! evaluate( entry ) ) { + return null; + } + return element; + } +); const { state } = store( 'directive-on-document', { state: { @@ -12,4 +25,10 @@ const { state } = store( 'directive-on-document', { state.counter += 1; }, }, + actions: { + visibilityHandler: () => { + const context = getContext(); + context.isVisible = ! context.isVisible; + }, + } } ); diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php index 3a7fcc5e8e9e6..e9c8792354e2a 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/render.php @@ -8,8 +8,11 @@ gutenberg_enqueue_module( 'directive-on-window-view' ); ?> -
-
-

0

+
+ +
+
+

0

+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js index 1fc1e938972de..11d01b7a216d1 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-on-window/view.js @@ -1,7 +1,20 @@ /** * WordPress dependencies */ -import { store } from '@wordpress/interactivity'; +import { store, directive, getContext } from '@wordpress/interactivity'; + +// Mock `data-wp-show` directive to test when things are removed from the +// DOM. Replace with `data-wp-show` when it's ready. +directive( + 'show-mock', + ( { directives: { 'show-mock': showMock }, element, evaluate } ) => { + const entry = showMock.find( ( { suffix } ) => suffix === 'default' ); + if ( ! evaluate( entry ) ) { + return null; + } + return element; + } +); const { state } = store( 'directive-on-window', { state: { @@ -12,4 +25,10 @@ const { state } = store( 'directive-on-window', { state.counter += 1; }, }, + actions: { + visibilityHandler: () => { + const context = getContext(); + context.isVisible = ! context.isVisible; + }, + } } ); diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.js index 9ab51b0ba9bdb..902925586645c 100644 --- a/packages/interactivity/src/directives.js +++ b/packages/interactivity/src/directives.js @@ -8,7 +8,7 @@ import { deepSignal, peek } from 'deepsignal'; * Internal dependencies */ import { createPortal } from './portals'; -import { useWatch, useInit, useEffect } from './utils'; +import { useWatch, useInit } from './utils'; import { directive } from './hooks'; const isObject = ( item ) => @@ -28,6 +28,64 @@ const mergeDeepSignals = ( target, source, overwrite ) => { } }; +const newRule = + /(?:([\u0080-\uFFFF\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\s*)/g; +const ruleClean = /\/\*[^]*?\*\/| +/g; +const ruleNewline = /\n+/g; +const empty = ' '; + +/** + * Convert a css style string into a object. + * + * Made by Cristian Bote (@cristianbote) for Goober. + * https://unpkg.com/browse/goober@2.1.13/src/core/astish.js + * + * @param {string} val CSS string. + * @return {Object} CSS object. + */ +const cssStringToObject = ( val ) => { + const tree = [ {} ]; + let block, left; + + while ( ( block = newRule.exec( val.replace( ruleClean, '' ) ) ) ) { + if ( block[ 4 ] ) { + tree.shift(); + } else if ( block[ 3 ] ) { + left = block[ 3 ].replace( ruleNewline, empty ).trim(); + tree.unshift( ( tree[ 0 ][ left ] = tree[ 0 ][ left ] || {} ) ); + } else { + tree[ 0 ][ block[ 1 ] ] = block[ 2 ] + .replace( ruleNewline, empty ) + .trim(); + } + } + + return tree[ 0 ]; +}; + +/** + * Creates a directive that adds an event listener to the global window or + * document object. + * + * @param {string} type 'window' or 'document' + * @return {void} + */ +const getGlobalEventDirective = + ( type ) => + ( { directives, evaluate } ) => { + directives[ `on-${ type }` ] + .filter( ( { suffix } ) => suffix !== 'default' ) + .forEach( ( entry ) => { + useInit( () => { + const cb = ( event ) => evaluate( entry, event ); + const globalVar = type === 'window' ? window : document; + globalVar.addEventListener( entry.suffix, cb ); + return () => + globalVar.removeEventListener( entry.suffix, cb ); + }, [] ); + } ); + }; + export default () => { // data-wp-context directive( @@ -87,22 +145,6 @@ export default () => { } ); } ); - const getGlobalEventDirective = - ( type ) => - ( { directives, evaluate } ) => { - directives[ `on-${ type }` ] - .filter( ( { suffix } ) => suffix !== 'default' ) - .forEach( ( entry ) => { - useEffect( () => { - const cb = ( event ) => evaluate( entry, event ); - const globalVar = type === 'window' ? window : document; - globalVar.addEventListener( entry.suffix, cb ); - return () => - globalVar.removeEventListener( entry.suffix, cb ); - }, [] ); - } ); - }; - // data-wp-on-window--[event] directive( 'on-window', getGlobalEventDirective( 'window' ) ); // data-wp-on-document--[event] @@ -145,41 +187,6 @@ export default () => { } ); - const newRule = - /(?:([\u0080-\uFFFF\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\s*)/g; - const ruleClean = /\/\*[^]*?\*\/| +/g; - const ruleNewline = /\n+/g; - const empty = ' '; - - /** - * Convert a css style string into a object. - * - * Made by Cristian Bote (@cristianbote) for Goober. - * https://unpkg.com/browse/goober@2.1.13/src/core/astish.js - * - * @param {string} val CSS string. - * @return {Object} CSS object. - */ - const cssStringToObject = ( val ) => { - const tree = [ {} ]; - let block, left; - - while ( ( block = newRule.exec( val.replace( ruleClean, '' ) ) ) ) { - if ( block[ 4 ] ) { - tree.shift(); - } else if ( block[ 3 ] ) { - left = block[ 3 ].replace( ruleNewline, empty ).trim(); - tree.unshift( ( tree[ 0 ][ left ] = tree[ 0 ][ left ] || {} ) ); - } else { - tree[ 0 ][ block[ 1 ] ] = block[ 2 ] - .replace( ruleNewline, empty ) - .trim(); - } - } - - return tree[ 0 ]; - }; - // data-wp-style--[style-key] directive( 'style', ( { directives: { style }, element, evaluate } ) => { style diff --git a/test/e2e/specs/interactivity/directive-on-document.spec.ts b/test/e2e/specs/interactivity/directive-on-document.spec.ts index e7333a9841938..918f3945e010f 100644 --- a/test/e2e/specs/interactivity/directive-on-document.spec.ts +++ b/test/e2e/specs/interactivity/directive-on-document.spec.ts @@ -26,4 +26,20 @@ test.describe( 'data-wp-on-document', () => { await page.keyboard.press( 'ArrowDown' ); await expect( counter ).toHaveText( '1' ); } ); + test( 'the event listener is removed when the element is removed', async ( { + page, + } ) => { + const counter = page.getByTestId( 'counter' ); + const visibilityButton = page.getByTestId( 'visibility' ); + await expect( counter ).toHaveText( '0' ); + await page.keyboard.press( 'ArrowDown' ); + await expect( counter ).toHaveText( '1' ); + // Remove the element. + await visibilityButton.click(); + // This keyboard press should not increase the counter. + await page.keyboard.press( 'ArrowDown' ); + // Add the element back. + await visibilityButton.click(); + await expect( counter ).toHaveText( '1' ); + } ); } ); diff --git a/test/e2e/specs/interactivity/directive-on-window.spec.ts b/test/e2e/specs/interactivity/directive-on-window.spec.ts index 991a44586c7e1..ff6abf04971b5 100644 --- a/test/e2e/specs/interactivity/directive-on-window.spec.ts +++ b/test/e2e/specs/interactivity/directive-on-window.spec.ts @@ -25,4 +25,19 @@ test.describe( 'data-wp-on-window', () => { const counter = page.getByTestId( 'counter' ); await expect( counter ).toHaveText( '1' ); } ); + test( 'the event listener is removed when the element is removed', async ( { + page, + } ) => { + const counter = page.getByTestId( 'counter' ); + const visibilityButton = page.getByTestId( 'visibility' ); + await page.setViewportSize( { width: 600, height: 600 } ); + await expect( counter ).toHaveText( '1' ); + // Remove the element. + await visibilityButton.click(); + // This resize should not increase the counter. + await page.setViewportSize( { width: 300, height: 600 } ); + // Add the element back. + await visibilityButton.click(); + await expect( counter ).toHaveText( '1' ); + } ); } ); From 4a86225dce1afe238f9ff127853ccb9b8c5683c2 Mon Sep 17 00:00:00 2001 From: Carlos Bravo Date: Thu, 18 Jan 2024 13:55:36 +0100 Subject: [PATCH 06/11] Add documentation --- .../interactivity/docs/2-api-reference.md | 164 +++++++++++++----- 1 file changed, 118 insertions(+), 46 deletions(-) diff --git a/packages/interactivity/docs/2-api-reference.md b/packages/interactivity/docs/2-api-reference.md index 7980c31b984f8..1b5245c802389 100644 --- a/packages/interactivity/docs/2-api-reference.md +++ b/packages/interactivity/docs/2-api-reference.md @@ -20,6 +20,8 @@ DOM elements are connected to data stored in the state and context through direc - [`wp-style`](#wp-style) ![](https://img.shields.io/badge/ATTRIBUTES-afd2e3.svg) - [`wp-text`](#wp-text) ![](https://img.shields.io/badge/CONTENT-afd2e3.svg) - [`wp-on`](#wp-on) ![](https://img.shields.io/badge/EVENT_HANDLERS-afd2e3.svg) + - [`wp-on-window`](#wp-on-window) ![](https://img.shields.io/badge/EVENT_HANDLERS-afd2e3.svg) + - [`wp-on-document`](#wp-on-document) ![](https://img.shields.io/badge/EVENT_HANDLERS-afd2e3.svg) - [`wp-watch`](#wp-watch) ![](https://img.shields.io/badge/SIDE_EFFECTS-afd2e3.svg) - [`wp-init`](#wp-init) ![](https://img.shields.io/badge/SIDE_EFFECTS-afd2e3.svg) - [`wp-run`](#wp-run) ![](https://img.shields.io/badge/SIDE_EFFECTS-afd2e3.svg) @@ -55,7 +57,7 @@ _Example of directives used in the HTML markup_ > Toggle - +

This element is now visible!

@@ -68,13 +70,13 @@ Directives can also be injected dynamically using the [HTML Tag Processor](https With directives, we can directly manage behavior related to things such as side effects, state, event handlers, attributes or content. -#### `wp-interactive` +#### `wp-interactive` The `wp-interactive` directive "activates" the interactivity for the DOM element and its children through the Interactivity API (directives and store). It includes a namespace to reference a specific store. ```html -
@@ -88,13 +90,13 @@ The `wp-interactive` directive "activates" the interactivity for the DOM element > **Note** > The use of `data-wp-interactive` is a requirement for the Interactivity API "engine" to work. In the following examples the `data-wp-interactive` has not been added for the sake of simplicity. Also, the `data-wp-interactive` directive will be injected automatically in the future. -#### `wp-context` +#### `wp-context` It provides a **local** state available to a specific HTML node and its children. The `wp-context` directive accepts a stringified JSON as a value. -_Example of `wp-context` directive_ +_Example of `wp-context` directive_ ```php //render.php @@ -139,13 +141,13 @@ Different contexts can be defined at different levels, and deeper levels will me
``` -#### `wp-bind` +#### `wp-bind` It allows setting HTML attributes on elements based on a boolean or string value. > This directive follows the syntax `data-wp-bind--attribute`. -_Example of `wp-bind` directive_ +_Example of `wp-bind` directive_ ```html
  • @@ -183,10 +185,10 @@ store( "myPlugin", { The `wp-bind` directive is executed: -- When the element is created. +- When the element is created. - Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference). -When `wp-bind` directive references a callback to get its final value: +When `wp-bind` directive references a callback to get its final value: - The `wp-bind` directive will be executed each time there's a change on any of the properties of the `state` or `context` used inside this callback. - The returned value in the callback function is used to change the value of the associated attribute. @@ -198,24 +200,24 @@ The `wp-bind` will do different things over the DOM element is applied, dependin - If the value is a string, the attribute is added with its value assigned: `
    `. -#### `wp-class` +#### `wp-class` It adds or removes a class to an HTML element, depending on a boolean value. > This directive follows the syntax `data-wp-class--classname`. -_Example of `wp-class` directive_ +_Example of `wp-class` directive_ ```html
    -
  • Option 1
  • -
  • This directive follows the syntax `data-wp-style--css-property`. -_Example of `wp-style` directive_ +_Example of `wp-style` directive_ ```html
    @@ -296,7 +298,7 @@ The value received by the directive is used to add or remove the style attribute - If the value is `false`, the style attribute is removed: `
    `. - If the value is a string, the attribute is added with its value assigned: `
    `. -#### `wp-text` +#### `wp-text` It sets the inner text of an HTML element. @@ -333,13 +335,13 @@ The `wp-text` directive is executed: The returned value is used to change the inner content of the element: `
    value
    `. -#### `wp-on` +#### `wp-on` -It runs code on dispatched DOM events like `click` or `keyup`. +It runs code on dispatched DOM events like `click` or `keyup`. > The syntax of this directive is `data-wp-on--[event]` (like `data-wp-on--click` or `data-wp-on--keyup`). -_Example of `wp-on` directive_ +_Example of `wp-on` directive_ ```php