From 6f2d112399b007ccf33e913183e36446d841c9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?= Date: Wed, 17 May 2023 10:45:07 +0200 Subject: [PATCH] File: Add experimental integration with Interactivity API (#50377) * File: Add experimental integration with Interactivity API * File: Mark the block as an island to activate directives * Improve webpack config * Refactor code with the latest changes applied to `trunk` * Refactor init effect with bind for `hidden` attribute --- bin/build-plugin-zip.sh | 2 +- lib/experimental/editor-settings.php | 3 - ...ion-block-interactivity.php => blocks.php} | 57 +++++++++++++------ .../interactivity-api/script-loader.php | 43 ++++++++------ lib/experiments-page.php | 8 +-- lib/load.php | 6 +- .../block-library/src/file/interactivity.js | 17 ++++++ .../src/file/{utils.js => utils/index.js} | 0 .../lib/index.js | 10 ++++ tools/webpack/blocks.js | 24 ++++++-- 10 files changed, 120 insertions(+), 50 deletions(-) rename lib/experimental/interactivity-api/{navigation-block-interactivity.php => blocks.php} (83%) create mode 100644 packages/block-library/src/file/interactivity.js rename packages/block-library/src/file/{utils.js => utils/index.js} (100%) diff --git a/bin/build-plugin-zip.sh b/bin/build-plugin-zip.sh index 52d473cd8d4016..131e434d1383d0 100755 --- a/bin/build-plugin-zip.sh +++ b/bin/build-plugin-zip.sh @@ -83,7 +83,7 @@ build_files=$( build/block-library/blocks/*.php \ build/block-library/blocks/*/block.json \ build/block-library/blocks/*/*.{js,js.map,css,asset.php} \ - build/block-library/interactive-blocks/*.js \ + build/block-library/interactivity/*.{js,js.map,asset.php} \ build/edit-widgets/blocks/*/block.json \ build/widgets/blocks/*.php \ build/widgets/blocks/*/block.json \ diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index 8d4046530fbc95..bf9acb7b70d4dd 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -95,9 +95,6 @@ function gutenberg_enable_experiments() { if ( $gutenberg_experiments && array_key_exists( 'gutenberg-details-blocks', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableDetailsBlocks = true', 'before' ); } - if ( $gutenberg_experiments && array_key_exists( 'gutenberg-interactivity-api-navigation-block', $gutenberg_experiments ) ) { - wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableNavigationBlockInteractivity = true', 'before' ); - } if ( $gutenberg_experiments && array_key_exists( 'gutenberg-theme-previews', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableThemePreviews = true', 'before' ); } diff --git a/lib/experimental/interactivity-api/navigation-block-interactivity.php b/lib/experimental/interactivity-api/blocks.php similarity index 83% rename from lib/experimental/interactivity-api/navigation-block-interactivity.php rename to lib/experimental/interactivity-api/blocks.php index 21e1e0381b70ed..755c1d1d4fa7d7 100644 --- a/lib/experimental/interactivity-api/navigation-block-interactivity.php +++ b/lib/experimental/interactivity-api/blocks.php @@ -6,6 +6,29 @@ * @package gutenberg */ +/** + * Adds Interactivity API directives to the File block markup using the Tag Processor. + * + * @param string $block_content Markup of the File block. + * @param array $block The full block, including name and attributes. + * @param WP_Block $instance The block instance. + * + * @return string File block markup with the directives injected when applicable. + */ +function gutenberg_block_core_file_add_directives_to_content( $block_content, $block, $instance ) { + if ( empty( $instance->attributes['displayPreview'] ) ) { + return $block_content; + } + $processor = new WP_HTML_Tag_Processor( $block_content ); + $processor->next_tag(); + $processor->set_attribute( 'data-wp-island', '' ); + $processor->next_tag( 'object' ); + $processor->set_attribute( 'data-wp-bind.hidden', 'selectors.core.file.hasNoPdfPreview' ); + $processor->set_attribute( 'hidden', true ); + return $processor->get_updated_html(); +} +add_filter( 'render_block_core/file', 'gutenberg_block_core_file_add_directives_to_content', 10, 3 ); + /** * Add Interactivity API directives to the navigation block markup using the Tag Processor * The final HTML of the navigation block will look similar to this: @@ -221,20 +244,20 @@ function gutenberg_block_core_navigation_add_directives_to_submenu( $w ) { add_filter( 'render_block_core/navigation', 'gutenberg_block_core_navigation_add_directives_to_markup', 10, 1 ); -// Enqueue the `interactivity.js` file with the store. -add_filter( - 'block_type_metadata', - function ( $metadata ) { - if ( 'core/navigation' === $metadata['name'] ) { - wp_register_script( - 'wp-block-navigation-view', - gutenberg_url( 'build/block-library/interactive-blocks/navigation.min.js' ), - array( 'wp-interactivity-runtime' ) - ); - $metadata['viewScript'] = array( 'wp-block-navigation-view' ); - } - return $metadata; - }, - 10, - 1 -); +/** + * Replaces view script for the File and Navigation blocks with version using Interactivity API. + * + * @param array $metadata Block metadata as read in via block.json. + * + * @return array Filtered block type metadata. + */ +function gutenberg_block_update_interactive_view_script( $metadata ) { + if ( + in_array( $metadata['name'], array( 'core/file', 'core/navigation' ), true ) && + str_contains( $metadata['file'], 'build/block-library/blocks' ) + ) { + $metadata['viewScript'] = array( 'file:./interactivity.min.js' ); + } + return $metadata; +} +add_filter( 'block_type_metadata', 'gutenberg_block_update_interactive_view_script', 10, 1 ); diff --git a/lib/experimental/interactivity-api/script-loader.php b/lib/experimental/interactivity-api/script-loader.php index 15804ca1e92444..63453713dd18cf 100644 --- a/lib/experimental/interactivity-api/script-loader.php +++ b/lib/experimental/interactivity-api/script-loader.php @@ -11,22 +11,28 @@ * @param WP_Scripts $scripts WP_Scripts instance. */ function gutenberg_register_interactivity_scripts( $scripts ) { - gutenberg_override_script( - $scripts, - 'wp-interactivity-runtime', - gutenberg_url( - 'build/block-library/interactive-blocks/interactivity.min.js' - ), - array( 'wp-interactivity-vendors' ) - ); + // When in production, use the plugin's version as the default asset version; + // else (for development or test) default to use the current time. + $default_version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time(); - gutenberg_override_script( - $scripts, - 'wp-interactivity-vendors', - gutenberg_url( - 'build/block-library/interactive-blocks/vendors.min.js' - ) - ); + foreach ( array( 'vendors', 'runtime' ) as $script_name ) { + $script_path = "build/block-library/interactivity/$script_name.min.js"; + // Replace extension with `.asset.php` to find the generated dependencies file. + $asset_file = gutenberg_dir_path() . substr( $script_path, 0, -( strlen( '.js' ) ) ) . '.asset.php'; + $asset = file_exists( $asset_file ) + ? require $asset_file + : null; + $dependencies = isset( $asset['dependencies'] ) ? $asset['dependencies'] : array(); + $version = isset( $asset['version'] ) ? $asset['version'] : $default_version; + + gutenberg_override_script( + $scripts, + "wp-interactivity-$script_name", + gutenberg_url( $script_path ), + $dependencies, + $version + ); + } } add_action( 'wp_default_scripts', 'gutenberg_register_interactivity_scripts', 10, 1 ); @@ -34,11 +40,12 @@ function gutenberg_register_interactivity_scripts( $scripts ) { * Adds the "defer" attribute to all the interactivity script tags. * * @param string $tag The generated script tag. + * @param string $handle The script handle. * * @return string The modified script tag. */ -function gutenberg_interactivity_scripts_add_defer_attribute( $tag ) { - if ( str_contains( $tag, '/block-library/interactive-blocks/' ) ) { +function gutenberg_interactivity_scripts_add_defer_attribute( $tag, $handle ) { + if ( str_starts_with( $handle, 'wp-interactivity-' ) || str_contains( $tag, '/interactivity.min.js' ) ) { $p = new WP_HTML_Tag_Processor( $tag ); $p->next_tag( array( 'tag' => 'script' ) ); $p->set_attribute( 'defer', true ); @@ -46,4 +53,4 @@ function gutenberg_interactivity_scripts_add_defer_attribute( $tag ) { } return $tag; } -add_filter( 'script_loader_tag', 'gutenberg_interactivity_scripts_add_defer_attribute', 10, 1 ); +add_filter( 'script_loader_tag', 'gutenberg_interactivity_scripts_add_defer_attribute', 10, 2 ); diff --git a/lib/experiments-page.php b/lib/experiments-page.php index e39a9dbefb9c16..521d04b75b34be 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -114,14 +114,14 @@ function gutenberg_initialize_experiments_settings() { ); add_settings_field( - 'gutenberg-interactivity-api-navigation-block', - __( 'Navigation block', 'gutenberg' ), + 'gutenberg-interactivity-api-core-blocks', + __( 'Core blocks', 'gutenberg' ), 'gutenberg_display_experiment_field', 'gutenberg-experiments', 'gutenberg_experiments_section', array( - 'label' => __( 'Test the Navigation block using the Interactivity API', 'gutenberg' ), - 'id' => 'gutenberg-interactivity-api-navigation-block', + 'label' => __( 'Test the core blocks using the Interactivity API', 'gutenberg' ), + 'id' => 'gutenberg-interactivity-api-core-blocks', ) ); diff --git a/lib/load.php b/lib/load.php index 31bf94d06a4641..b8ec4a4d607849 100644 --- a/lib/load.php +++ b/lib/load.php @@ -99,13 +99,13 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/experimental/block-editor-settings-mobile.php'; require __DIR__ . '/experimental/block-editor-settings.php'; require __DIR__ . '/experimental/blocks.php'; -require __DIR__ . '/experimental/interactivity-api/script-loader.php'; require __DIR__ . '/experimental/navigation-theme-opt-in.php'; require __DIR__ . '/experimental/kses.php'; require __DIR__ . '/experimental/l10n.php'; require __DIR__ . '/experimental/navigation-fallback.php'; -if ( gutenberg_is_experiment_enabled( 'gutenberg-interactivity-api-navigation-block' ) ) { - require __DIR__ . '/experimental/interactivity-api/navigation-block-interactivity.php'; +if ( gutenberg_is_experiment_enabled( 'gutenberg-interactivity-api-core-blocks' ) ) { + require __DIR__ . '/experimental/interactivity-api/script-loader.php'; + require __DIR__ . '/experimental/interactivity-api/blocks.php'; } // Fonts API. diff --git a/packages/block-library/src/file/interactivity.js b/packages/block-library/src/file/interactivity.js new file mode 100644 index 00000000000000..cf9ae41002b276 --- /dev/null +++ b/packages/block-library/src/file/interactivity.js @@ -0,0 +1,17 @@ +/** + * Internal dependencies + */ +import { store } from '../utils/interactivity'; +import { browserSupportsPdfs } from './utils'; + +store( { + selectors: { + core: { + file: { + hasNoPdfPreview() { + return ! browserSupportsPdfs(); + }, + }, + }, + }, +} ); diff --git a/packages/block-library/src/file/utils.js b/packages/block-library/src/file/utils/index.js similarity index 100% rename from packages/block-library/src/file/utils.js rename to packages/block-library/src/file/utils/index.js diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 581274c3684f93..3da2286ddbd57d 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -27,6 +27,7 @@ class DependencyExtractionWebpackPlugin { combinedOutputFile: null, externalizedReport: false, injectPolyfill: false, + __experimentalInjectInteractivityRuntime: false, outputFormat: 'php', outputFilename: null, useDefaults: true, @@ -142,6 +143,7 @@ class DependencyExtractionWebpackPlugin { combinedOutputFile, externalizedReport, injectPolyfill, + __experimentalInjectInteractivityRuntime, outputFormat, outputFilename, } = this.options; @@ -184,6 +186,14 @@ class DependencyExtractionWebpackPlugin { if ( injectPolyfill ) { chunkDeps.add( 'wp-polyfill' ); } + // Temporary fix for Interactivity API until it gets moved to its package. + if ( __experimentalInjectInteractivityRuntime ) { + if ( ! chunkJSFile.startsWith( './interactivity/' ) ) { + chunkDeps.add( 'wp-interactivity-runtime' ); + } else if ( './interactivity/runtime.min.js' === chunkJSFile ) { + chunkDeps.add( 'wp-interactivity-vendors' ); + } + } const processModule = ( { userRequest } ) => { if ( this.externalizedDeps.has( userRequest ) ) { diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js index d8bcf0559fdbd7..481b8b457a1967 100644 --- a/tools/webpack/blocks.js +++ b/tools/webpack/blocks.js @@ -220,16 +220,23 @@ module.exports = [ ].filter( Boolean ), }, { + ...baseConfig, + watchOptions: { + aggregateTimeout: 200, + }, + name: 'interactivity', entry: { + file: './packages/block-library/src/file/interactivity.js', navigation: './packages/block-library/src/navigation/interactivity.js', }, output: { devtoolNamespace: 'wp', - filename: './build/block-library/interactive-blocks/[name].min.js', - path: join( __dirname, '..', '..' ), + filename: './blocks/[name]/interactivity.min.js', + path: join( __dirname, '..', '..', 'build', 'block-library' ), }, optimization: { + ...baseConfig.optimization, runtimeChunk: { name: 'vendors', }, @@ -238,12 +245,14 @@ module.exports = [ vendors: { name: 'vendors', test: /[\\/]node_modules[\\/]/, + filename: './interactivity/[name].min.js', minSize: 0, chunks: 'all', }, - interactivity: { - name: 'interactivity', + runtime: { + name: 'runtime', test: /[\\/]utils\/interactivity[\\/]/, + filename: './interactivity/[name].min.js', chunks: 'all', minSize: 0, priority: -10, @@ -279,5 +288,12 @@ module.exports = [ }, ], }, + plugins: [ + ...plugins, + new DependencyExtractionWebpackPlugin( { + __experimentalInjectInteractivityRuntime: true, + injectPolyfill: false, + } ), + ].filter( Boolean ), }, ];