diff --git a/.github/workflows/tests-e2e.yml b/.github/workflows/tests-e2e.yml index c0145b4..a2d1be2 100644 --- a/.github/workflows/tests-e2e.yml +++ b/.github/workflows/tests-e2e.yml @@ -96,7 +96,8 @@ jobs: matrix: include: - php: '8.1' - core: 'WordPress/WordPress#6.2' + core: 'WordPress/WordPress#6.4' + # null = latest official WordPress release (wp-env default). - php: '8.3' core: null steps: diff --git a/.github/workflows/tests-php.yml b/.github/workflows/tests-php.yml index da978f3..a483f5b 100644 --- a/.github/workflows/tests-php.yml +++ b/.github/workflows/tests-php.yml @@ -57,10 +57,11 @@ jobs: fail-fast: false matrix: php: ['8.1', '8.3'] + # null = latest official WordPress release (wp-env default). core: [null] include: - php: '8.1' - core: 'WordPress/WordPress#6.2' + core: 'WordPress/WordPress#6.4' steps: - name: Checkout uses: actions/checkout@v4 diff --git a/README.md b/README.md index 1027c84..7601356 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ An accessible tabs block for WordPress Gutenberg editor that follows ARIA best p ## Requirements -- WordPress 6.2 or higher +- WordPress 6.4 or higher - PHP 8.1 or higher - Gutenberg editor enabled @@ -54,17 +54,23 @@ By default, the block comes with 3 tabs. Each tab contains a panel where you can 1. Select a tab item 2. In the block toolbar, click the "Icon" button -3. Choose an icon from the available options (native WordPress `core/icon` block) +3. Choose an icon from a registered icon block -To keep supporting Blockparty Icons / BeAPI Icon Block instead of (or in addition to) `core/icon`, use the `blockparty_tabs_allowed_icon_blocks` filter: +By default the plugin allows, in order: + +1. `core/icon` (WordPress 7.0+) +2. `blockparty/icon` +3. `beapi/icon-block` + +Only registered blocks are kept, so on WordPress 6.4–6.9 icons work when Blockparty Icons or BeAPI Icon Block is active. On WordPress 7.0+, `core/icon` is preferred when present. + +Customize the list with the `blockparty_tabs_allowed_icon_blocks` filter: ```php add_filter( 'blockparty_tabs_allowed_icon_blocks', - static function ( array $blocks ): array { - $blocks[] = 'blockparty/icon'; - $blocks[] = 'beapi/icon-block'; - return $blocks; + static function (): array { + return [ 'core/icon' ]; } ); ``` diff --git a/blockparty-tabs.php b/blockparty-tabs.php index 7174a7b..0e88431 100644 --- a/blockparty-tabs.php +++ b/blockparty-tabs.php @@ -2,7 +2,7 @@ /** * Plugin Name: Blockparty Tabs * Description: Accessible Tabs block for WordPress gutenberg. - * Requires at least: 6.2 + * Requires at least: 6.4 * Requires PHP: 8.1 * Version: 1.1.5 * Author: Be API Technical team @@ -32,9 +32,17 @@ function init(): void { add_action( 'init', __NAMESPACE__ . '\\init' ); /** - * Default icon block used inside tab nav items. + * Default icon blocks allowed inside tab nav items. + * + * `core/icon` ships in WordPress 7.0+. Legacy Blockparty / BeAPI icon blocks + * remain in the default list so icons keep working on the required 6.4+ range + * whenever those plugins are active. The editor keeps only registered names. */ -const BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK = 'core/icon'; +const BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS = [ + 'core/icon', + 'blockparty/icon', + 'beapi/icon-block', +]; /** * Returns the icon block names allowed inside tab nav items. @@ -45,15 +53,14 @@ function get_allowed_icon_blocks(): array { /** * Filters the icon block types allowed inside tab nav items. * - * By default only `core/icon` is allowed. To keep supporting - * Blockparty Icons / BeAPI Icon Block as before: + * Defaults prefer `core/icon` (WordPress 7.0+) and include + * `blockparty/icon` / `beapi/icon-block` for older installs. Example — + * allow only the native icon block: * * add_filter( * 'blockparty_tabs_allowed_icon_blocks', - * static function ( array $blocks ): array { - * $blocks[] = 'blockparty/icon'; - * $blocks[] = 'beapi/icon-block'; - * return $blocks; + * static function (): array { + * return [ 'core/icon' ]; * } * ); * @@ -64,11 +71,11 @@ function get_allowed_icon_blocks(): array { */ $blocks = apply_filters( 'blockparty_tabs_allowed_icon_blocks', - [ BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK ] + BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS ); if ( ! is_array( $blocks ) ) { - return [ BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK ]; + return BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS; } $sanitized = []; @@ -87,7 +94,7 @@ function get_allowed_icon_blocks(): array { $sanitized = array_values( array_unique( $sanitized ) ); - return [] === $sanitized ? [ BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK ] : $sanitized; + return [] === $sanitized ? BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS : $sanitized; } /** diff --git a/composer.json b/composer.json index 3b15749..da67fe1 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "phpunit/phpunit": "9.6.37", "roave/security-advisories": "dev-latest", "wp-coding-standards/wpcs": "^3.0", - "wp-phpunit/wp-phpunit": "6.8.9", + "wp-phpunit/wp-phpunit": "7.1.1", "yoast/phpunit-polyfills": "4.0.0" }, "scripts": { diff --git a/readme.txt b/readme.txt index 7a887fe..df1f8bf 100644 --- a/readme.txt +++ b/readme.txt @@ -1,8 +1,10 @@ === Blockparty Tabs === Contributors: Be API Technical team -Tags: block -Tested up to: 6.0 -Stable tag: 1.1.5 +Tags: block, tabs, accessibility +Requires at least: 6.4 +Requires PHP: 8.1 +Tested up to: 7.1 +Stable tag: 2.0.0 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/src/blockparty-tabs-nav-item/getAllowedIconBlocks.js b/src/blockparty-tabs-nav-item/getAllowedIconBlocks.js index f5226e6..a25e2b8 100644 --- a/src/blockparty-tabs-nav-item/getAllowedIconBlocks.js +++ b/src/blockparty-tabs-nav-item/getAllowedIconBlocks.js @@ -1,6 +1,17 @@ import { getBlockType } from '@wordpress/blocks'; -const BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK = 'core/icon'; +/** + * Default icon blocks allowed inside tab nav items. + * + * `core/icon` ships in WordPress 7.0+. Legacy Blockparty / BeAPI icon blocks + * remain so icons keep working on WordPress 6.4–6.9 when those plugins are + * active. Only registered names are kept by getRegisteredIconBlocks(). + */ +export const BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS = [ + 'core/icon', + 'blockparty/icon', + 'beapi/icon-block', +]; /** * Legacy Blockparty / BeAPI icon blocks expect width + maxIcons. @@ -33,7 +44,7 @@ export const getRegisteredIconBlocks = () => { const candidates = Array.isArray( fromPhp ) && fromPhp.length > 0 ? fromPhp - : [ BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK ]; + : BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS; return candidates.filter( ( blockName ) => typeof getBlockType( blockName ) !== 'undefined' diff --git a/tests/js/getAllowedIconBlocks.test.js b/tests/js/getAllowedIconBlocks.test.js index f33237f..40083f3 100644 --- a/tests/js/getAllowedIconBlocks.test.js +++ b/tests/js/getAllowedIconBlocks.test.js @@ -6,6 +6,7 @@ * Internal dependencies */ import { + BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS, getIconTemplateAttributes, getRegisteredIconBlocks, } from '../../src/blockparty-tabs-nav-item/getAllowedIconBlocks'; @@ -41,12 +42,30 @@ describe( 'getRegisteredIconBlocks', () => { delete window.blockpartyTabsSettings; } ); - it( 'defaults to core/icon when settings are missing', () => { + it( 'defaults to registered candidates from the shared default list', () => { getBlockType.mockImplementation( ( name ) => - name === 'core/icon' ? { name } : undefined + name === 'blockparty/icon' ? { name } : undefined ); - expect( getRegisteredIconBlocks() ).toEqual( [ 'core/icon' ] ); + expect( BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS ).toEqual( [ + 'core/icon', + 'blockparty/icon', + 'beapi/icon-block', + ] ); + expect( getRegisteredIconBlocks() ).toEqual( [ 'blockparty/icon' ] ); + } ); + + it( 'prefers core/icon when it is registered among defaults', () => { + getBlockType.mockImplementation( ( name ) => + [ 'core/icon', 'blockparty/icon' ].includes( name ) + ? { name } + : undefined + ); + + expect( getRegisteredIconBlocks() ).toEqual( [ + 'core/icon', + 'blockparty/icon', + ] ); } ); it( 'keeps only registered blocks from PHP settings', () => { @@ -77,4 +96,10 @@ describe( 'getRegisteredIconBlocks', () => { expect( getRegisteredIconBlocks() ).toEqual( [] ); } ); + + it( 'returns an empty list on WordPress without core/icon or legacy plugins', () => { + getBlockType.mockReturnValue( undefined ); + + expect( getRegisteredIconBlocks() ).toEqual( [] ); + } ); } ); diff --git a/tests/php/AllowedIconBlocksTest.php b/tests/php/AllowedIconBlocksTest.php index 56d2b0d..16a6814 100644 --- a/tests/php/AllowedIconBlocksTest.php +++ b/tests/php/AllowedIconBlocksTest.php @@ -9,7 +9,7 @@ use WP_UnitTestCase; use function Blockparty\Tabs\get_allowed_icon_blocks; -use const Blockparty\Tabs\BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK; +use const Blockparty\Tabs\BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS; /** * @covers ::Blockparty\Tabs\get_allowed_icon_blocks @@ -27,9 +27,34 @@ public function tearDown(): void { /** * @return void */ - public function test_defaults_to_core_icon(): void { + public function test_defaults_include_core_and_legacy_icon_blocks(): void { $this->assertSame( - [ BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK ], + BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS, + get_allowed_icon_blocks() + ); + $this->assertSame( + [ + 'core/icon', + 'blockparty/icon', + 'beapi/icon-block', + ], + get_allowed_icon_blocks() + ); + } + + /** + * @return void + */ + public function test_filter_can_replace_icon_blocks(): void { + add_filter( + 'blockparty_tabs_allowed_icon_blocks', + static function (): array { + return [ 'core/icon' ]; + } + ); + + $this->assertSame( + [ 'core/icon' ], get_allowed_icon_blocks() ); } @@ -41,8 +66,7 @@ public function test_filter_can_append_icon_blocks(): void { add_filter( 'blockparty_tabs_allowed_icon_blocks', static function ( array $blocks ): array { - $blocks[] = 'blockparty/icon'; - $blocks[] = 'beapi/icon-block'; + $blocks[] = 'my-plugin/custom-icon'; return $blocks; } ); @@ -52,6 +76,7 @@ static function ( array $blocks ): array { 'core/icon', 'blockparty/icon', 'beapi/icon-block', + 'my-plugin/custom-icon', ], get_allowed_icon_blocks() ); @@ -97,7 +122,7 @@ static function () { ); $this->assertSame( - [ BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK ], + BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS, get_allowed_icon_blocks() ); } @@ -114,7 +139,7 @@ static function (): array { ); $this->assertSame( - [ BLOCKPARTY_TABS_DEFAULT_ICON_BLOCK ], + BLOCKPARTY_TABS_DEFAULT_ICON_BLOCKS, get_allowed_icon_blocks() ); } diff --git a/tests/playwright/specs/editor-tabs.spec.js b/tests/playwright/specs/editor-tabs.spec.js index 17bb105..5643807 100644 --- a/tests/playwright/specs/editor-tabs.spec.js +++ b/tests/playwright/specs/editor-tabs.spec.js @@ -6,9 +6,8 @@ import { test, expect } from '../utils/fixtures'; /** * Resolve the block editor content root. * - * WordPress 6.3+ uses an iframed canvas (`editor-canvas`). Older versions render - * blocks in the parent document. `FrameLocator.or()` is unreliable when the - * iframe is absent, so detect the canvas mode explicitly. + * WordPress 6.4+ uses an iframed canvas (`editor-canvas`). Detect the canvas + * mode explicitly: `FrameLocator.or()` is unreliable when the iframe is absent. * * @param {import('@playwright/test').Page} page */ diff --git a/webpack.config.js b/webpack.config.js index 3a804b5..8b808b2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,7 +10,7 @@ const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); /** * Polyfill for the `react-jsx-runtime` script handle added in WordPress 6.6. * Modern @wordpress/scripts builds declare this dependency; without it, editor - * scripts never load on WordPress 6.2–6.5 and blocks stay unregistered. + * scripts never load on WordPress 6.4–6.5 and blocks stay unregistered. * * Compiled after the main blocks config (`dependencies`) so `output.clean` from * `@wordpress/scripts` cannot delete the polyfill asset in a parallel race.