From 6be06ecf9ee8b5b2d707989eb70e3536579c93e4 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 15:09:47 +0200 Subject: [PATCH 01/11] Add e2e plugin to test the style directive Co-authored-by: Carlos Bravo --- .../directive-style/block.json | 14 +++ .../directive-style/render.php | 87 +++++++++++++++++++ .../directive-style/view.js | 25 ++++++ 3 files changed, 126 insertions(+) create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php create mode 100644 packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json b/packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json new file mode 100644 index 0000000000000..6cbfa57b0784f --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/block.json @@ -0,0 +1,14 @@ +{ + "apiVersion": 2, + "name": "test/directive-style", + "title": "E2E Interactivity tests - directive style", + "category": "text", + "icon": "heart", + "description": "", + "supports": { + "interactivity": true + }, + "textdomain": "e2e-interactivity", + "viewScript": "directive-style-view", + "render": "file:./render.php" +} diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php new file mode 100644 index 0000000000000..18d89fd114e80 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php @@ -0,0 +1,87 @@ + + +
+ + + + + + +
Don't change style if callback returns same value
+ +
Remove style if callback returns falsy value
+ +
Change style if callback returns a new value
+ +
Handles multiple styles and callbacks
+ +
Handles styles names with hyphens
+ +
Can toggle style in the middle
+ +
Can toggle style when style attribute is missing
+ +
+
+ +
+
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js new file mode 100644 index 0000000000000..cd1b275b24688 --- /dev/null +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js @@ -0,0 +1,25 @@ +( ( { wp } ) => { + const { store } = wp.interactivity; + + store( { + state: { + falseValue: false, + color: "red", + border: "2px solid yellow" + }, + actions: { + toggleColor: ( { state } ) => { + state.color = state.color === "red" ? "blue" : "red"; + }, + switchColorToFalse: ({ state }) => { + state.color = false; + }, + toggleBorder: ( { state } ) => { + state.color = state.color === "2px solid yellow" ? "1px solid black" : "2px solid yellow"; + }, + toggleContextValue: ( { context } ) => { + context.color = context.color === "red" ? "blue" : "red"; + }, + }, + } ); +} )( window ); From 2e4ca04687e4e2e9abbae68d58973449e745d00d Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 15:11:17 +0200 Subject: [PATCH 02/11] Add style directive runtime --- packages/interactivity/src/directives.js | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.js index ca87f7655dba6..1f712ae497fa2 100644 --- a/packages/interactivity/src/directives.js +++ b/packages/interactivity/src/directives.js @@ -139,6 +139,73 @@ 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://github.com/cristianbote/goober/blob/a849b2d644146d96fa1dd1c560f6418ee1e1c469/src/core/astish.js + * + * @param {string} val CSS string. + * @return {Object} CSS object. + */ + const astish = ( 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, context } ) => { + const contextValue = useContext( context ); + Object.keys( style ) + .filter( ( n ) => n !== 'default' ) + .forEach( ( key ) => { + const result = evaluate( style[ key ], { + key, + context: contextValue, + } ); + element.props.style = element.props.style || {}; + if ( typeof element.props.style === 'string' ) + element.props.style = astish( element.props.style ); + if ( ! result ) delete element.props.style[ key ]; + else element.props.style[ key ] = result; + + useEffect( () => { + // This seems necessary because Preact doesn't change the styles on + // the hydration, so we have to do it manually. It doesn't need deps + // because it only needs to do it the first time. + if ( ! result ) { + element.ref.current.style.removeProperty( key ); + } else { + element.ref.current.style[ key ] = result; + } + }, [] ); + } ); + } + ); + // data-wp-bind--[attribute] directive( 'bind', From 34ad4d96038824b8df5d52a02ecdb5daeebafbb8 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 17:18:11 +0200 Subject: [PATCH 03/11] Add specs --- .../directive-style/render.php | 50 +++++--- .../directive-style/view.js | 2 +- .../interactivity/directives-style.spec.ts | 118 ++++++++++++++++++ tools/webpack/interactivity.js | 4 + 4 files changed, 154 insertions(+), 20 deletions(-) create mode 100644 test/e2e/specs/interactivity/directives-style.spec.ts diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php index 18d89fd114e80..c8f2a6767beb8 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php @@ -31,45 +31,57 @@
Don't change style if callback returns same value
+ data-testid="dont change style if callback returns same value on hydration" + >Don't change style if callback returns same value on hydration
Remove style if callback returns falsy value
+ data-testid="remove style if callback returns falsy value on hydration" + >Remove style if callback returns falsy value on hydration
Change style if callback returns a new value
+ data-testid="change style if callback returns a new value on hydration" + >Change style if callback returns a new value on hydration
Handles multiple styles and callbacks
+ data-testid="handles multiple styles and callbacks on hydration" + >Handles multiple styles and callbacks on hydration
Handles styles names with hyphens
+ data-wp-style--color="state.color" + data-testid="can add style when style attribute is missing on hydration" + >Can add style when style attribute is missing on hydration
Can toggle style
+ +
Can remove style
+ +
Can toggle style in the middle
Can toggle style when style attribute is missing
+ style="background-color: green;" + data-wp-style--background-color="state.color" + data-testid="handles styles names with hyphens" + >Handles styles names with hyphens
diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js index cd1b275b24688..3dfa49444584a 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js @@ -17,7 +17,7 @@ toggleBorder: ( { state } ) => { state.color = state.color === "2px solid yellow" ? "1px solid black" : "2px solid yellow"; }, - toggleContextValue: ( { context } ) => { + toggleContext: ( { context } ) => { context.color = context.color === "red" ? "blue" : "red"; }, }, diff --git a/test/e2e/specs/interactivity/directives-style.spec.ts b/test/e2e/specs/interactivity/directives-style.spec.ts new file mode 100644 index 0000000000000..033f6b26057c5 --- /dev/null +++ b/test/e2e/specs/interactivity/directives-style.spec.ts @@ -0,0 +1,118 @@ +/** + * Internal dependencies + */ +import { test, expect } from './fixtures'; + +test.describe( 'data-wp-style', () => { + test.beforeAll( async ( { interactivityUtils: utils } ) => { + await utils.activatePlugins(); + await utils.addPostWithBlock( 'test/directive-style' ); + } ); + + test.beforeEach( async ( { interactivityUtils: utils, page } ) => { + await page.goto( utils.getLink( 'test/directive-style' ) ); + } ); + + test.afterAll( async ( { interactivityUtils: utils } ) => { + await utils.deactivatePlugins(); + await utils.deleteAllPosts(); + } ); + + test( 'dont change style if callback returns same value on hydration', async ( { + page, + } ) => { + const el = page.getByTestId( + 'dont change style if callback returns same value on hydration' + ); + await expect( el ).toHaveAttribute( + 'style', + 'color: red; background: green;' + ); + } ); + + test( 'remove style if callback returns falsy value on hydration', async ( { + page, + } ) => { + const el = page.getByTestId( + 'remove style if callback returns falsy value on hydration' + ); + await expect( el ).toHaveAttribute( 'style', 'background: green;' ); + } ); + + test( 'change style if callback returns a new value on hydration', async ( { + page, + } ) => { + const el = page.getByTestId( + 'change style if callback returns a new value on hydration' + ); + await expect( el ).toHaveAttribute( + 'style', + 'color: red; background: green;' + ); + } ); + + test( 'handles multiple styles and callbacks on hydration', async ( { + page, + } ) => { + const el = page.getByTestId( + 'handles multiple styles and callbacks on hydration' + ); + await expect( el ).toHaveAttribute( + 'style', + 'background: red; border: 2px solid yellow;' + ); + } ); + + test( 'can add style when style attribute is missing on hydration', async ( { + page, + } ) => { + const el = page.getByTestId( + 'can add style when style attribute is missing on hydration' + ); + await expect( el ).toHaveAttribute( 'style', 'color: red;' ); + } ); + + test( 'can toggle style', async ( { page } ) => { + const el = page.getByTestId( 'can toggle style' ); + await expect( el ).toHaveAttribute( 'style', 'color: red;' ); + await page.getByTestId( 'toggle color' ).click(); + await expect( el ).toHaveAttribute( 'style', 'color: blue;' ); + } ); + + test( 'can remove style', async ( { page } ) => { + const el = page.getByTestId( 'can remove style' ); + await expect( el ).toHaveAttribute( 'style', 'color: red;' ); + await page.getByTestId( 'switch color to false' ).click(); + await expect( el ).toHaveAttribute( 'style', '' ); + } ); + + test( 'can toggle style in the middle', async ( { page } ) => { + const el = page.getByTestId( 'can toggle style in the middle' ); + await expect( el ).toHaveAttribute( + 'style', + 'color: blue; background: red; border: 1px solid black;' + ); + await page.getByTestId( 'toggle color' ).click(); + await expect( el ).toHaveAttribute( + 'style', + 'color: blue; background: blue; border: 1px solid black;' + ); + } ); + + test( 'handles styles names with hyphens', async ( { page } ) => { + const el = page.getByTestId( 'handles styles names with hyphens' ); + await expect( el ).toHaveAttribute( 'style', 'background-color: red;' ); + await page.getByTestId( 'toggle color' ).click(); + await expect( el ).toHaveAttribute( + 'style', + 'background-color: blue;' + ); + } ); + + test( 'can use context values', async ( { page } ) => { + const el = page.getByTestId( 'can use context values' ); + await expect( el ).toHaveAttribute( 'style', 'color: blue;' ); + await page.getByTestId( 'toggle context' ).click(); + await expect( el ).toHaveAttribute( 'style', 'color: red;' ); + } ); +} ); diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js index 715d824979e5e..26e49966ad40c 100644 --- a/tools/webpack/interactivity.js +++ b/tools/webpack/interactivity.js @@ -53,4 +53,8 @@ module.exports = { }, ], }, + watchOptions: { + ignored: [ '**/node_modules' ], + aggregateTimeout: 500, + }, }; From 79435eb6ab6ea96b9ec98cb5d0db46971491bc16 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 17:22:07 +0200 Subject: [PATCH 04/11] Add changelog --- packages/interactivity/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 packages/interactivity/CHANGELOG.md diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md new file mode 100644 index 0000000000000..ae41ce05be64f --- /dev/null +++ b/packages/interactivity/CHANGELOG.md @@ -0,0 +1,7 @@ + + +## Unreleased + +### New Features + +- Runtime support for the `data-wp-style` directive. ([#XX](XX)) From 5a2c3ba4e06c22cf34e2dad6f3e02030965f6f45 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 17:22:40 +0200 Subject: [PATCH 05/11] Remove unnecessary action --- .../plugins/interactive-blocks/directive-style/view.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js index 3dfa49444584a..04fbf1aab1140 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/view.js @@ -14,9 +14,6 @@ switchColorToFalse: ({ state }) => { state.color = false; }, - toggleBorder: ( { state } ) => { - state.color = state.color === "2px solid yellow" ? "1px solid black" : "2px solid yellow"; - }, toggleContext: ( { context } ) => { context.color = context.color === "red" ? "blue" : "red"; }, From 25e9354e623cccc4f793a8881350cd5b1faa59c5 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 17:30:13 +0200 Subject: [PATCH 06/11] Rename astish to cssStringToObject --- packages/interactivity/src/directives.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.js index 1f712ae497fa2..a2344d10fe941 100644 --- a/packages/interactivity/src/directives.js +++ b/packages/interactivity/src/directives.js @@ -154,7 +154,7 @@ export default () => { * @param {string} val CSS string. * @return {Object} CSS object. */ - const astish = ( val ) => { + const cssStringToObject = ( val ) => { const tree = [ {} ]; let block, left; @@ -188,7 +188,9 @@ export default () => { } ); element.props.style = element.props.style || {}; if ( typeof element.props.style === 'string' ) - element.props.style = astish( element.props.style ); + element.props.style = cssStringToObject( + element.props.style + ); if ( ! result ) delete element.props.style[ key ]; else element.props.style[ key ] = result; From 45954dc5ef3615816ef4bb1b16178e20ae4ae9af Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 17:31:14 +0200 Subject: [PATCH 07/11] Undo webpack changes (leaked from other PR) --- tools/webpack/interactivity.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js index 26e49966ad40c..715d824979e5e 100644 --- a/tools/webpack/interactivity.js +++ b/tools/webpack/interactivity.js @@ -53,8 +53,4 @@ module.exports = { }, ], }, - watchOptions: { - ignored: [ '**/node_modules' ], - aggregateTimeout: 500, - }, }; From b3325bbaecf79962ab6a1a15fc43e39598e4ad4e Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 17:33:30 +0200 Subject: [PATCH 08/11] Update PR number --- packages/interactivity/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index ae41ce05be64f..50fe48f82ba89 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -4,4 +4,4 @@ ### New Features -- Runtime support for the `data-wp-style` directive. ([#XX](XX)) +- Runtime support for the `data-wp-style` directive. ([#52645](https://github.com/WordPress/gutenberg/pull/52645)) From 585df468231e91faa051abca222425986eb00794 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 14 Jul 2023 17:35:35 +0200 Subject: [PATCH 09/11] Remove unnecessary button --- .../plugins/interactive-blocks/directive-style/render.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php index c8f2a6767beb8..bd125a9f50467 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php @@ -21,13 +21,6 @@ Switch Color to False - -
Date: Fri, 14 Jul 2023 17:37:40 +0200 Subject: [PATCH 10/11] Fix PHP lint error --- .../plugins/interactive-blocks/directive-style/render.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php index bd125a9f50467..4272333c3ec27 100644 --- a/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php +++ b/packages/e2e-tests/plugins/interactive-blocks/directive-style/render.php @@ -4,6 +4,7 @@ * * @package gutenberg-test-interactive-blocks */ + ?>
From 49c06bbdcef9da79958ab2e48a43de7689e6e01f Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Mon, 17 Jul 2023 10:36:14 +0200 Subject: [PATCH 11/11] Update astish link to make it shorter --- packages/interactivity/src/directives.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.js index a2344d10fe941..e32d691c29fed 100644 --- a/packages/interactivity/src/directives.js +++ b/packages/interactivity/src/directives.js @@ -149,7 +149,7 @@ export default () => { * Convert a css style string into a object. * * Made by Cristian Bote (@cristianbote) for Goober. - * https://github.com/cristianbote/goober/blob/a849b2d644146d96fa1dd1c560f6418ee1e1c469/src/core/astish.js + * https://unpkg.com/browse/goober@2.1.13/src/core/astish.js * * @param {string} val CSS string. * @return {Object} CSS object.