From bbd04d80077c64d6f9d036e14d44796d7938ed6b Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 13 Jun 2023 13:04:26 +0000 Subject: [PATCH 001/163] Bump plugin version to 16.0.0-rc.5 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index 39597f4200be09..1dc4d96e5c5ce2 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.1 * Requires PHP: 5.6 - * Version: 16.0.0-rc.4 + * Version: 16.0.0-rc.5 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 7def9279b709b8..a985c7f3c6c09c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.0.0-rc.4", + "version": "16.0.0-rc.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index ecef5003f433df..42cf39859a920b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.0.0-rc.4", + "version": "16.0.0-rc.5", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From 3ae552e933a777fb6ab3b926bb2996ca66c587b8 Mon Sep 17 00:00:00 2001 From: Ang Ngima Lama Sherpa <89342874+xerpa43@users.noreply.github.com> Date: Tue, 13 Jun 2023 18:55:25 +0545 Subject: [PATCH 002/163] fixed error 'Avatar block not 1:1 between the editor and the front-end #49775' (#49963) --- packages/block-library/src/avatar/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/avatar/style.scss b/packages/block-library/src/avatar/style.scss index 1f1bd7c97e8426..ed6d846afad0ec 100644 --- a/packages/block-library/src/avatar/style.scss +++ b/packages/block-library/src/avatar/style.scss @@ -1,6 +1,7 @@ .wp-block-avatar { // This block has customizable padding, border-box makes that more predictable. box-sizing: border-box; + line-height: 0; img { box-sizing: border-box; } From 01e82cb91e63627abb0782a39e1fddea7cdb732a Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 13 Jun 2023 13:15:06 +0000 Subject: [PATCH 003/163] Update Changelog for 16.0.0-rc.5 --- changelog.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/changelog.txt b/changelog.txt index cc76024cfaf118..c57c24c569bd1f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,29 @@ == Changelog == += 16.0.0-rc.5 = + + + +## Changelog + +### Bug Fixes + +#### Layout +- Make sure post content always has correct layout. ([51431](https://github.com/WordPress/gutenberg/pull/51431)) + +#### Accessibility +- Block Toolbar: Fix text only label for locked blocks. ([50944](https://github.com/WordPress/gutenberg/pull/50944)) + + + + +## Contributors + +The following contributors merged PRs in this release: + +@Mamaduka @tellthemachines + + = 16.0.0-rc.4 = From f173d643a486b576c820e17cddbd0fa6556a5fd1 Mon Sep 17 00:00:00 2001 From: Lucio Giannotta Date: Tue, 13 Jun 2023 18:31:54 +0300 Subject: [PATCH 004/163] Fix site editor rendering of Categories block (#51329) The Categories block would not render properly within the site editor. Specifically: - Prefix and Suffix options were not shown when the block was selected. - Separator was not available in the advanced panel. - Alignment toolbar was not available in the toolbar. --- packages/block-library/src/post-terms/edit.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/block-library/src/post-terms/edit.js b/packages/block-library/src/post-terms/edit.js index d46c738e79ec4e..49e3a4ce801f42 100644 --- a/packages/block-library/src/post-terms/edit.js +++ b/packages/block-library/src/post-terms/edit.js @@ -69,10 +69,6 @@ export default function PostTermsEdit( { } ), } ); - if ( ! hasPost || ! term ) { - return
{ blockInformation.title }
; - } - return ( <> @@ -96,7 +92,7 @@ export default function PostTermsEdit( { />
- { isLoading && } + { isLoading && hasPost && } { ! isLoading && hasPostTerms && ( isSelected || prefix ) && ( ) } - { ! isLoading && + { ( ! hasPost || ! term ) && ( + { blockInformation.title } + ) } + { hasPost && + ! isLoading && hasPostTerms && postTerms .map( ( postTerm ) => ( @@ -132,7 +132,8 @@ export default function PostTermsEdit( { { curr } ) ) } - { ! isLoading && + { hasPost && + ! isLoading && ! hasPostTerms && ( selectedTerm?.labels?.no_terms || __( 'Term items not found.' ) ) } From 11dc8b92f214df63628bef1ab3c0d2f5ee27444f Mon Sep 17 00:00:00 2001 From: Phill <38789408+SavPhill@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:24:13 +0700 Subject: [PATCH 005/163] Documentation: Update broken link (#41758) * Update broken link in docs/getting-started/devenv/docker-ubuntu.md Co-authored-by: Noah Allen --- docs/getting-started/devenv/docker-ubuntu.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/devenv/docker-ubuntu.md b/docs/getting-started/devenv/docker-ubuntu.md index f26ca0826140ef..680798d74f4e8c 100644 --- a/docs/getting-started/devenv/docker-ubuntu.md +++ b/docs/getting-started/devenv/docker-ubuntu.md @@ -34,7 +34,7 @@ If docker is not running, try to start the service using: sudo systemctl start docker.service ``` -If docker is running, then it is not listening how the WordPress environment is trying to communicate. Try adding the following service override file to include listening on tcp. See docker documentation, [How do I enable the remote API for dockerd](https://success.docker.com/article/how-do-i-enable-the-remote-api-for-dockerd) +If docker is running, then it is not listening how the WordPress environment is trying to communicate. Try adding the following service override file to include listening on tcp. See [this Docker documentation](https://docs.docker.com/config/daemon/remote-access/) on how to configure remote access for Docker daemon. ``` # /etc/systemd/system/docker.service.d/override.conf From 97379fdb21df8f9d57ac2706dec7c64ee255f1eb Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 13 Jun 2023 23:09:00 +0400 Subject: [PATCH 006/163] Add myself as one of the code owners for Playwright tests (#51470) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c29a18dcee2e04..37d1abb5256f2a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -78,7 +78,7 @@ /packages/prettier-config @ntwb @gziolo /packages/scripts @gziolo @ntwb @nerrad @ajitbohra @ryanwelcher /packages/stylelint-config @ntwb -/test/e2e @kevin940726 +/test/e2e @kevin940726 @Mamaduka # UI Components /packages/components @ajitbohra From 05a229b3977a4e1277f0548514b05906c92a981b Mon Sep 17 00:00:00 2001 From: Alex Stine Date: Tue, 13 Jun 2023 16:06:13 -0500 Subject: [PATCH 007/163] List View: A11Y focus enhancements for edit-site based on modifications to edit-post (#51404) * Bring A11Y changes to site editor list view from the post editor. Add E2E testing. * Add missing comment to explain usage of open-only keyboard shortcut usage. * Reviewer feedback. --- .../keyboard-shortcuts/edit-mode.js | 6 +- .../components/keyboard-shortcuts/index.js | 6 +- .../secondary-sidebar/list-view-sidebar.js | 57 ++++++++- test/e2e/specs/site-editor/list-view.spec.js | 108 ++++++++++++++++++ 4 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 test/e2e/specs/site-editor/list-view.spec.js diff --git a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js index 7d491d54932f53..10b198eb73d348 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js @@ -76,8 +76,12 @@ function KeyboardShortcutsEditMode() { event.preventDefault(); } ); + // Only opens the list view. Other functionality for this shortcut happens in the rendered sidebar. useShortcut( 'core/edit-site/toggle-list-view', () => { - setIsListViewOpened( ! isListViewOpen ); + if ( isListViewOpen ) { + return; + } + setIsListViewOpened( true ); } ); useShortcut( 'core/edit-site/toggle-block-settings-sidebar', ( event ) => { diff --git a/packages/edit-site/src/components/keyboard-shortcuts/index.js b/packages/edit-site/src/components/keyboard-shortcuts/index.js index fa4b1aa6833dc3..0beecbb3430084 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/index.js @@ -93,8 +93,12 @@ function KeyboardShortcuts() { event.preventDefault(); } ); + // Only opens the list view. Other functionality for this shortcut happens in the rendered sidebar. useShortcut( 'core/edit-site/toggle-list-view', () => { - setIsListViewOpened( ! isListViewOpen ); + if ( ! isListViewOpen ) { + return; + } + setIsListViewOpened( true ); } ); useShortcut( 'core/edit-site/toggle-block-settings-sidebar', ( event ) => { diff --git a/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js index 7a19cdc08db114..0e46e99decd2f5 100644 --- a/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js @@ -9,10 +9,12 @@ import { useMergeRefs, } from '@wordpress/compose'; import { useDispatch } from '@wordpress/data'; -import { useState } from '@wordpress/element'; +import { useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { closeSmall } from '@wordpress/icons'; import { ESCAPE } from '@wordpress/keycodes'; +import { focus } from '@wordpress/dom'; +import { useShortcut } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -25,11 +27,9 @@ const { PrivateListView } = unlock( blockEditorPrivateApis ); export default function ListViewSidebar() { const { setIsListViewOpened } = useDispatch( editSiteStore ); - // Use internal state instead of a ref to make sure that the component - // re-renders when the dropZoneElement updates. - const [ dropZoneElement, setDropZoneElement ] = useState( null ); - + // This hook handles focus when the sidebar first renders. const focusOnMountRef = useFocusOnMount( 'firstElement' ); + // The next 2 hooks handle focus for when the sidebar closes and returning focus to the element that had focus before sidebar opened. const headerFocusReturnRef = useFocusReturn(); const contentFocusReturnRef = useFocusReturn(); @@ -39,11 +39,56 @@ export default function ListViewSidebar() { } } + // Use internal state instead of a ref to make sure that the component + // re-renders when the dropZoneElement updates. + const [ dropZoneElement, setDropZoneElement ] = useState( null ); + + // This ref refers to the sidebar as a whole. + const sidebarRef = useRef(); + // This ref refers to the close button. + const sidebarCloseButtonRef = useRef(); + // This ref refers to the list view application area. + const listViewRef = useRef(); + + /* + * Callback function to handle list view or close button focus. + * + * @return void + */ + function handleSidebarFocus() { + // Either focus the list view or the sidebar close button. Must have a fallback because the list view does not render when there are no blocks. + const listViewApplicationFocus = focus.tabbable.find( + listViewRef.current + )[ 0 ]; + const listViewFocusArea = sidebarRef.current.contains( + listViewApplicationFocus + ) + ? listViewApplicationFocus + : sidebarCloseButtonRef.current; + listViewFocusArea.focus(); + } + + // This only fires when the sidebar is open because of the conditional rendering. It is the same shortcut to open but that is defined as a global shortcut and only fires when the sidebar is closed. + useShortcut( 'core/edit-site/toggle-list-view', () => { + // If the sidebar has focus, it is safe to close. + if ( + sidebarRef.current.contains( + sidebarRef.current.ownerDocument.activeElement + ) + ) { + setIsListViewOpened( false ); + // If the list view or close button does not have focus, focus should be moved to it. + } else { + handleSidebarFocus(); + } + } ); + return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions
setIsListViewOpened( false ) } + ref={ sidebarCloseButtonRef } />
diff --git a/test/e2e/specs/site-editor/list-view.spec.js b/test/e2e/specs/site-editor/list-view.spec.js new file mode 100644 index 00000000000000..2cecc34950dd0a --- /dev/null +++ b/test/e2e/specs/site-editor/list-view.spec.js @@ -0,0 +1,108 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Site Editor List View', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'emptytheme' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test.beforeEach( async ( { admin, editor } ) => { + // Select a template part with a few blocks. + await admin.visitSiteEditor( { + postId: 'emptytheme//header', + postType: 'wp_template_part', + } ); + await editor.canvas.click( 'body' ); + } ); + + // If list view sidebar is open and focus is not inside the sidebar, move + // focus to the sidebar when using the shortcut. If focus is inside the + // sidebar, shortcut should close the sidebar. + test( 'ensures List View global shortcut works properly', async ( { + editor, + page, + pageUtils, + } ) => { + // Current starting focus should be at Open Navigation button. + const openNavigationButton = page.getByRole( 'button', { + name: 'Open Navigation', + exact: true, + } ); + await openNavigationButton.focus(); + await expect( openNavigationButton ).toBeFocused(); + + // Open List View. + await pageUtils.pressKeys( 'access+o' ); + const listView = page.getByRole( 'treegrid', { + name: 'Block navigation structure', + } ); + await expect( listView ).toBeVisible(); + + // The site title block should have focus. + await expect( + listView.getByRole( 'link', { + name: 'Site Title', + exact: true, + } ) + ).toBeFocused(); + + // Navigate to the site tagline block. + await page.keyboard.press( 'ArrowDown' ); + const siteTaglineItem = listView.getByRole( 'link', { + name: 'Site Tagline', + exact: true, + } ); + await expect( siteTaglineItem ).toBeFocused(); + + // Hit enter to focus the site tagline block in the canvas. + await page.keyboard.press( 'Enter' ); + await expect( + editor.canvas.getByRole( 'document', { + name: 'Block: Site Tagline', + } ) + ).toBeFocused(); + + // Since focus is now at the site tagline block in the canvas, + // pressing the list view shortcut should bring focus back to the site tagline + // block in the list view. + await pageUtils.pressKeys( 'access+o' ); + await expect( siteTaglineItem ).toBeFocused(); + + // Since focus is now inside the list view, the shortcut should close + // the sidebar. + await pageUtils.pressKeys( 'access+o' ); + await expect( listView ).not.toBeVisible(); + + // Focus should now be on the Open Navigation button since that is + // where we opened the list view sidebar. This is not a perfect + // solution, but current functionality prevents a better way at + // the moment. + await expect( openNavigationButton ).toBeFocused(); + + // Open List View. + await pageUtils.pressKeys( 'access+o' ); + await expect( listView ).toBeVisible(); + + // Focus the list view close button and make sure the shortcut will + // close the list view. This is to catch a bug where elements could be + // out of range of the sidebar region. Must shift+tab 1 time to reach + // close button before list view area. + await pageUtils.pressKeys( 'shift+Tab' ); + await expect( + page + .getByRole( 'region', { name: 'List View' } ) + .getByRole( 'button', { + name: 'Close', + } ) + ).toBeFocused(); + await pageUtils.pressKeys( 'access+o' ); + await expect( listView ).not.toBeVisible(); + await expect( openNavigationButton ).toBeFocused(); + } ); +} ); From 664a5afc31106f63c01cbaca5914a5ff22b029a9 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 14 Jun 2023 09:18:05 +0400 Subject: [PATCH 008/163] Block Editor: Improve data selector for BlockQuickNavigationItem (#51429) * Block Editor: Improve data selector for BlockQuickNavigationItem * Remove unnecessary 'key' prop --- .../block-quick-navigation/index.js | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/block-editor/src/components/block-quick-navigation/index.js b/packages/block-editor/src/components/block-quick-navigation/index.js index 35f24255b9ba67..de33c8a427f257 100644 --- a/packages/block-editor/src/components/block-quick-navigation/index.js +++ b/packages/block-editor/src/components/block-quick-navigation/index.js @@ -8,7 +8,10 @@ import { __experimentalHStack as HStack, FlexItem, } from '@wordpress/components'; -import { getBlockType, __experimentalGetBlockLabel } from '@wordpress/blocks'; +import { + __experimentalGetBlockLabel, + store as blocksStore, +} from '@wordpress/blocks'; /** * Internal dependencies @@ -33,7 +36,7 @@ export default function BlockQuickNavigation( { clientIds } ) { } function BlockQuickNavigationItem( { clientId } ) { - const { name, attributes, isSelected } = useSelect( + const { name, icon, isSelected } = useSelect( ( select ) => { const { getBlockName, @@ -41,9 +44,20 @@ function BlockQuickNavigationItem( { clientId } ) { isBlockSelected, hasSelectedInnerBlock, } = select( blockEditorStore ); + const { getBlockType } = select( blocksStore ); + + const blockType = getBlockType( getBlockName( clientId ) ); + const attributes = getBlockAttributes( clientId ); + return { - name: getBlockName( clientId ), - attributes: getBlockAttributes( clientId ), + name: + blockType && + __experimentalGetBlockLabel( + blockType, + attributes, + 'list-view' + ), + icon: blockType?.icon, isSelected: isBlockSelected( clientId ) || hasSelectedInnerBlock( clientId, /* deep: */ true ), @@ -52,22 +66,15 @@ function BlockQuickNavigationItem( { clientId } ) { [ clientId ] ); const { selectBlock } = useDispatch( blockEditorStore ); - const blockType = getBlockType( name ); + return ( ); From b6986e0b603c29d65f7cdac9dea17de64cef48bf Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 14 Jun 2023 15:19:23 +1000 Subject: [PATCH 009/163] Global styles revisions: move from experimental to 6.3 (#51474) * Removing `gutenberg_update_global_styles_rest_controller` hook as it's not required for 6.3 and was unrelated to the addition of the /revisions endpoint in the first place Moving global styles revisions endpoint file to 6-3 since it'll be included in 6.3 and is not experimental * Class needs to be named after file name, hence the 6_3 suffix * Backporting changes from core to gutenberg: adding prepare_date_response to parse dates * Tearing down after class in tests * Post name should be different to the one created in the global styles endpoint test :) --- ...lobal-styles-revisions-controller-6-3.php} | 43 ++++++++++++++----- lib/compat/wordpress-6.3/rest-api.php | 16 +------ lib/load.php | 2 +- ...lobal-styles-revisions-controller-test.php | 16 +++++-- 4 files changed, 47 insertions(+), 30 deletions(-) rename lib/{experimental/class-gutenberg-rest-global-styles-revisions-controller.php => compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php} (85%) diff --git a/lib/experimental/class-gutenberg-rest-global-styles-revisions-controller.php b/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php similarity index 85% rename from lib/experimental/class-gutenberg-rest-global-styles-revisions-controller.php rename to lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php index c5d5a5f0dff1a4..356b435b71f1e8 100644 --- a/lib/experimental/class-gutenberg-rest-global-styles-revisions-controller.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php @@ -14,7 +14,7 @@ * * @see WP_REST_Controller */ -class Gutenberg_REST_Global_Styles_Revisions_Controller extends WP_REST_Controller { +class Gutenberg_REST_Global_Styles_Revisions_Controller_6_3 extends WP_REST_Controller { /** * Parent post type. * @@ -107,19 +107,42 @@ public function get_items( $request ) { return rest_ensure_response( $response ); } + /** + * A direct copy of WP_REST_Revisions_Controller->prepare_date_response(). + * Checks the post_date_gmt or modified_gmt and prepare any post or + * modified date for single post output. + * + * @since 6.3.0 + * + * @param string $date_gmt GMT publication time. + * @param string|null $date Optional. Local publication time. Default null. + * @return string|null ISO8601/RFC3339 formatted datetime, otherwise null. + */ + protected function prepare_date_response( $date_gmt, $date = null ) { + if ( '0000-00-00 00:00:00' === $date_gmt ) { + return null; + } + + if ( isset( $date ) ) { + return mysql_to_rfc3339( $date ); + } + + return mysql_to_rfc3339( $date_gmt ); + } + /** * Prepares the revision for the REST response. * * @since 6.3.0 * - * @param WP_Post $item Post revision object. + * @param WP_Post $post Post revision object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response Response object. */ - public function prepare_item_for_response( $item, $request ) { + public function prepare_item_for_response( $post, $request ) { $parent = $this->get_parent( $request['parent'] ); // Retrieves global styles config as JSON. - $raw_revision_config = json_decode( $item->post_content, true ); + $raw_revision_config = json_decode( $post->post_content, true ); $config = ( new WP_Theme_JSON_Gutenberg( $raw_revision_config, 'custom' ) )->get_raw_data(); // Prepares item data. @@ -127,27 +150,27 @@ public function prepare_item_for_response( $item, $request ) { $fields = $this->get_fields_for_response( $request ); if ( rest_is_field_included( 'author', $fields ) ) { - $data['author'] = (int) $item->post_author; + $data['author'] = (int) $post->post_author; } if ( rest_is_field_included( 'date', $fields ) ) { - $data['date'] = $item->post_date; + $data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date ); } if ( rest_is_field_included( 'date_gmt', $fields ) ) { - $data['date_gmt'] = $item->post_date_gmt; + $data['date_gmt'] = $this->prepare_date_response( $post->post_date_gmt ); } if ( rest_is_field_included( 'id', $fields ) ) { - $data['id'] = (int) $item->ID; + $data['id'] = (int) $post->ID; } if ( rest_is_field_included( 'modified', $fields ) ) { - $data['modified'] = $item->post_modified; + $data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ); } if ( rest_is_field_included( 'modified_gmt', $fields ) ) { - $data['modified_gmt'] = $item->post_modified_gmt; + $data['modified_gmt'] = $this->prepare_date_response( $post->post_modified_gmt ); } if ( rest_is_field_included( 'parent', $fields ) ) { diff --git a/lib/compat/wordpress-6.3/rest-api.php b/lib/compat/wordpress-6.3/rest-api.php index 50f8eeff6c3230..d571e56dae7628 100644 --- a/lib/compat/wordpress-6.3/rest-api.php +++ b/lib/compat/wordpress-6.3/rest-api.php @@ -39,7 +39,7 @@ function gutenberg_update_templates_template_parts_rest_controller( $args, $post * Registers the Global Styles Revisions REST API routes. */ function gutenberg_register_global_styles_revisions_endpoints() { - $global_styles_revisions_controller = new Gutenberg_REST_Global_Styles_Revisions_Controller(); + $global_styles_revisions_controller = new Gutenberg_REST_Global_Styles_Revisions_Controller_6_3(); $global_styles_revisions_controller->register_routes(); } add_action( 'rest_api_init', 'gutenberg_register_global_styles_revisions_endpoints' ); @@ -53,17 +53,3 @@ function gutenberg_register_global_styles_endpoints() { } add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); -/** - * Update `wp_global_styles` post type to use Gutenberg's REST controller. - * - * @param array $args Array of arguments for registering a post type. - * @param string $post_type Post type key. - */ -function gutenberg_update_global_styles_rest_controller( $args, $post_type ) { - if ( in_array( $post_type, array( 'wp_global_styles' ), true ) ) { - $args['rest_controller_class'] = 'Gutenberg_REST_Templates_Controller_6_3'; - $args['rest_base'] = 'global-styles'; - } - return $args; -} -add_filter( 'register_post_type_args', 'gutenberg_update_global_styles_rest_controller', 10, 2 ); diff --git a/lib/load.php b/lib/load.php index f5c1f25db52522..1fd251bd19e9b0 100644 --- a/lib/load.php +++ b/lib/load.php @@ -46,6 +46,7 @@ function gutenberg_is_experiment_enabled( $name ) { // WordPress 6.3 compat. require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php'; + require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/rest-api.php'; require_once __DIR__ . '/compat/wordpress-6.3/theme-previews.php'; require_once __DIR__ . '/compat/wordpress-6.3/navigation-block-preloading.php'; @@ -57,7 +58,6 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/experimental/class-wp-rest-customizer-nonces.php'; } - require_once __DIR__ . '/experimental/class-gutenberg-rest-global-styles-revisions-controller.php'; require_once __DIR__ . '/experimental/class-wp-rest-navigation-fallback-controller.php'; require_once __DIR__ . '/experimental/rest-api.php'; } diff --git a/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php b/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php index 6856638bf8754e..3e7f8763c5c765 100644 --- a/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php +++ b/phpunit/class-gutenberg-rest-global-styles-revisions-controller-test.php @@ -48,21 +48,29 @@ public static function wpSetupBeforeClass( $factory ) { ) ); // This creates the global styles for the current theme. - self::$global_styles_id = wp_insert_post( + self::$global_styles_id = $factory->post->create( array( 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', 'post_status' => 'publish', 'post_title' => __( 'Custom Styles', 'default' ), 'post_type' => 'wp_global_styles', - 'post_name' => 'wp-global-styles-emptytheme', + 'post_name' => 'wp-global-styles-emptytheme-revisions', 'tax_input' => array( 'wp_theme' => 'emptytheme', ), - ), - true + ) ); } + /** + * Removes users after our tests run. + */ + public static function wpTearDownAfterClass() { + self::delete_user( self::$admin_id ); + self::delete_user( self::$second_admin_id ); + self::delete_user( self::$author_id ); + } + /** * @covers Gutenberg_REST_Global_Styles_Revisions_Controller::register_routes */ From b9be5b9dd0050c39577dcd6657011e5f18dc04ad Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Wed, 14 Jun 2023 19:34:39 +1200 Subject: [PATCH 010/163] Post editor: Make the Post Content block available as a child of the Query block (#51405) * Only remove the post content block if the parent is not the query block * update logic to check for parent Query Loop block * Merge into a single filter * Split back up into two filters * fix comment --------- Co-authored-by: ntsekouras --- packages/edit-post/src/index.js | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 156c434e7a3761..116dea17a5c04e 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -79,7 +79,7 @@ export function initializeEditor( } /* - * Prevent adding template part and post content block in the post editor. + * Prevent adding template part in the post editor. * Only add the filter when the post editor is initialized, not imported. * Also only add the filter(s) after registerCoreBlocks() * so that common filters in the block library are not overwritten. @@ -90,8 +90,7 @@ export function initializeEditor( ( canInsert, blockType ) => { if ( ! select( editPostStore ).isEditingTemplate() && - ( blockType.name === 'core/template-part' || - blockType.name === 'core/post-content' ) + blockType.name === 'core/template-part' ) { return false; } @@ -99,6 +98,34 @@ export function initializeEditor( } ); + /* + * Prevent adding post content block (except in query block) in the post editor. + * Only add the filter when the post editor is initialized, not imported. + * Also only add the filter(s) after registerCoreBlocks() + * so that common filters in the block library are not overwritten. + */ + addFilter( + 'blockEditor.__unstableCanInsertBlockType', + 'removePostContentFromInserter', + ( + canInsert, + blockType, + rootClientId, + { getBlockParentsByBlockName } + ) => { + if ( + ! select( editPostStore ).isEditingTemplate() && + blockType.name === 'core/post-content' + ) { + return ( + getBlockParentsByBlockName( rootClientId, 'core/query' ) + .length > 0 + ); + } + return canInsert; + } + ); + // Show a console log warning if the browser is not in Standards rendering mode. const documentMode = document.compatMode === 'CSS1Compat' ? 'Standards' : 'Quirks'; From 078ede8ae69a29ec5f6792afba37e18c8d3f833a Mon Sep 17 00:00:00 2001 From: Phill <38789408+SavPhill@users.noreply.github.com> Date: Wed, 14 Jun 2023 15:12:18 +0700 Subject: [PATCH 011/163] Preferences: Adjust the name of the custom field button labels (#47407) --- .../preferences-modal/options/enable-custom-fields.js | 4 ++-- .../options/test/__snapshots__/enable-custom-fields.js.snap | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/edit-post/src/components/preferences-modal/options/enable-custom-fields.js b/packages/edit-post/src/components/preferences-modal/options/enable-custom-fields.js index 063875786c3aff..9b1442a947f76c 100644 --- a/packages/edit-post/src/components/preferences-modal/options/enable-custom-fields.js +++ b/packages/edit-post/src/components/preferences-modal/options/enable-custom-fields.js @@ -31,8 +31,8 @@ export function CustomFieldsConfirmation( { willEnable } ) { } } > { willEnable - ? __( 'Enable & Reload' ) - : __( 'Disable & Reload' ) } + ? __( 'Show & Reload Page' ) + : __( 'Hide & Reload Page' ) } ); diff --git a/packages/edit-post/src/components/preferences-modal/options/test/__snapshots__/enable-custom-fields.js.snap b/packages/edit-post/src/components/preferences-modal/options/test/__snapshots__/enable-custom-fields.js.snap index a405a1c39ee1cd..781cff283f1646 100644 --- a/packages/edit-post/src/components/preferences-modal/options/test/__snapshots__/enable-custom-fields.js.snap +++ b/packages/edit-post/src/components/preferences-modal/options/test/__snapshots__/enable-custom-fields.js.snap @@ -100,7 +100,7 @@ exports[`EnableCustomFieldsOption renders a checked checkbox and a confirmation class="components-button edit-post-preferences-modal__custom-fields-confirmation-button is-secondary" type="button" > - Enable & Reload + Show & Reload Page
@@ -303,7 +303,7 @@ exports[`EnableCustomFieldsOption renders an unchecked checkbox and a confirmati class="components-button edit-post-preferences-modal__custom-fields-confirmation-button is-secondary" type="button" > - Disable & Reload + Hide & Reload Page
From 4eca1dc47de8534f62ecc2f08324355a04dc624b Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 14 Jun 2023 12:13:00 +0400 Subject: [PATCH 012/163] Formats: Avoid rerendering language edit component when typing (#51440) --- packages/format-library/src/language/index.js | 139 +++++++++--------- 1 file changed, 73 insertions(+), 66 deletions(-) diff --git a/packages/format-library/src/language/index.js b/packages/format-library/src/language/index.js index 664f28126c1f2c..77e6334016f732 100644 --- a/packages/format-library/src/language/index.js +++ b/packages/format-library/src/language/index.js @@ -29,21 +29,10 @@ export const language = { title, }; -function Edit( props ) { - const { contentRef, isActive, onChange, value } = props; - const popoverAnchor = useAnchor( { - editableContentElement: contentRef.current, - settings: language, - } ); - - const [ lang, setLang ] = useState( '' ); - const [ dir, setDir ] = useState( 'ltr' ); - +function Edit( { isActive, value, onChange, contentRef } ) { const [ isPopoverVisible, setIsPopoverVisible ] = useState( false ); const togglePopover = () => { setIsPopoverVisible( ( state ) => ! state ); - setLang( '' ); - setDir( 'ltr' ); }; return ( @@ -62,63 +51,81 @@ function Edit( props ) { isActive={ isActive } role="menuitemcheckbox" /> - { isPopoverVisible && ( - -
{ - onChange( - applyFormat( value, { - type: name, - attributes: { - lang, - dir, - }, - } ) - ); - togglePopover(); - event.preventDefault(); - } } - > - setLang( val ) } - help={ __( - 'A valid language attribute, like "en" or "fr".' - ) } - /> - setDir( val ) } - /> - - - ); } @@ -129,8 +154,8 @@ export const withBehaviors = createHigherOrderComponent( ( BlockEdit ) => { { - if ( nextValue === undefined ) { + onChangeBehavior={ ( nextValue ) => { + if ( nextValue === 'default' ) { props.setAttributes( { behaviors: undefined, } ); @@ -139,11 +164,29 @@ export const withBehaviors = createHigherOrderComponent( ( BlockEdit ) => { // change the default value (true) so we save it in the attributes. props.setAttributes( { behaviors: { - lightbox: nextValue === 'lightbox', + lightbox: { + enabled: nextValue === 'lightbox', + animation: + nextValue === 'lightbox' + ? 'zoom' + : '', + }, }, } ); } } } + onChangeAnimation={ ( nextValue ) => { + props.setAttributes( { + behaviors: { + lightbox: { + enabled: + props.attributes.behaviors.lightbox + .enabled, + animation: nextValue, + }, + }, + } ); + } } disabled={ blockHasLink } /> diff --git a/packages/block-library/src/image/interactivity.js b/packages/block-library/src/image/interactivity.js index 552bdf13a66ca2..2ef370496a894e 100644 --- a/packages/block-library/src/image/interactivity.js +++ b/packages/block-library/src/image/interactivity.js @@ -21,28 +21,70 @@ store( { actions: { core: { image: { - showLightbox: ( { context } ) => { + showLightbox: ( { context, event } ) => { context.core.image.initialized = true; - context.core.image.lightboxEnabled = true; context.core.image.lastFocusedElement = window.document.activeElement; - context.core.image.scrollPosition = window.scrollY; - document.documentElement.classList.add( - 'has-lightbox-open' - ); + context.core.image.scrollDelta = 0; + + // Since the img is hidden and its src not loaded until + // the lightbox is opened, let's create an img element on the fly + // so we can get the dimensions we need to calculate the styles + const imgDom = document.createElement( 'img' ); + + imgDom.onload = function () { + // Enable the lightbox only after the image + // is loaded to prevent flashing of unstyled content + context.core.image.lightboxEnabled = true; + if ( context.core.image.lightboxAnimation === 'zoom' ) { + setZoomStyles( imgDom, context, event ); + } + + // Hide overflow only when the animation is in progress, + // otherwise the removal of the scrollbars will draw attention + // to itself and look like an error + document.documentElement.classList.add( + 'has-lightbox-open' + ); + }; + imgDom.setAttribute( 'src', context.core.image.imageSrc ); }, hideLightbox: async ( { context, event } ) => { + context.core.image.hideAnimationEnabled = true; if ( context.core.image.lightboxEnabled ) { // If scrolling, wait a moment before closing the lightbox. - if ( - event.type === 'mousewheel' && - Math.abs( - window.scrollY - - context.core.image.scrollPosition - ) < 5 + if ( context.core.image.lightboxAnimation === 'fade' ) { + context.core.image.scrollDelta += event.deltaY; + if ( + event.type === 'mousewheel' && + Math.abs( + window.scrollY - + context.core.image.scrollDelta + ) < 10 + ) { + return; + } + } else if ( + context.core.image.lightboxAnimation === 'zoom' ) { - return; + // Disable scroll until the zoom animation ends. + // Get the current page scroll position + const scrollTop = + window.pageYOffset || + document.documentElement.scrollTop; + const scrollLeft = + window.pageXOffset || + document.documentElement.scrollLeft; + // if any scroll is attempted, set this to the previous value. + window.onscroll = function () { + window.scrollTo( scrollLeft, scrollTop ); + }; + // Enable scrolling after the animation finishes + setTimeout( function () { + window.onscroll = function () {}; + }, 400 ); } + document.documentElement.classList.remove( 'has-lightbox-open' ); @@ -101,6 +143,9 @@ store( { core: { image: { initLightbox: async ( { context, ref } ) => { + context.core.image.figureRef = + ref.querySelector( 'figure' ); + context.core.image.imageRef = ref.querySelector( 'img' ); if ( context.core.image.lightboxEnabled ) { const focusableElements = ref.querySelectorAll( focusableSelectors ); @@ -116,3 +161,100 @@ store( { }, }, } ); + +function setZoomStyles( imgDom, context, event ) { + let targetWidth = imgDom.naturalWidth; + let targetHeight = imgDom.naturalHeight; + + const verticalPadding = 40; + + // As per the design, let's allow the image to stretch + // to the full width of its containing figure, but for the height, + // constrain it with a fixed padding + const containerWidth = context.core.image.figureRef.clientWidth; + + // The lightbox image has `positione:absolute` and + // ignores its parent's padding, so let's set the padding here, + // to be used when calculating the image width and positioning + let horizontalPadding = 0; + if ( containerWidth > 480 ) { + horizontalPadding = 40; + } else if ( containerWidth > 1920 ) { + horizontalPadding = 80; + } + + const containerHeight = + context.core.image.figureRef.clientHeight - verticalPadding * 2; + + // Check difference between the image and figure dimensions + const widthOverflow = Math.abs( + Math.min( containerWidth - targetWidth, 0 ) + ); + const heightOverflow = Math.abs( + Math.min( containerHeight - targetHeight, 0 ) + ); + + // If image is larger than its container any dimension, resize along its largest axis. + // For vertically oriented devices, always maximize the width. + if ( widthOverflow > 0 || heightOverflow > 0 ) { + if ( + widthOverflow >= heightOverflow || + containerHeight >= containerWidth + ) { + targetWidth = containerWidth - horizontalPadding * 2; + targetHeight = + imgDom.naturalHeight * ( targetWidth / imgDom.naturalWidth ); + } else { + targetHeight = containerHeight; + targetWidth = + imgDom.naturalWidth * ( targetHeight / imgDom.naturalHeight ); + } + } + + // The reference img element lies adjacent to the event target button in the DOM + const { x: originLeft, y: originTop } = + event.target.nextElementSibling.getBoundingClientRect(); + const scaleWidth = + event.target.nextElementSibling.offsetWidth / targetWidth; + const scaleHeight = + event.target.nextElementSibling.offsetHeight / targetHeight; + + // Get values used to center the image + let targetLeft = 0; + if ( targetWidth >= containerWidth ) { + targetLeft = horizontalPadding; + } else { + targetLeft = ( containerWidth - targetWidth ) / 2; + } + let targetTop = 0; + if ( targetHeight >= containerHeight ) { + targetTop = verticalPadding; + } else { + targetTop = ( containerHeight - targetHeight ) / 2 + verticalPadding; + } + + const root = document.documentElement; + root.style.setProperty( '--lightbox-scale-width', scaleWidth ); + root.style.setProperty( '--lightbox-scale-height', scaleHeight ); + root.style.setProperty( '--lightbox-image-max-width', targetWidth + 'px' ); + root.style.setProperty( + '--lightbox-image-max-height', + targetHeight + 'px' + ); + root.style.setProperty( + '--lightbox-initial-left-position', + originLeft + 'px' + ); + root.style.setProperty( + '--lightbox-initial-top-position', + originTop + 'px' + ); + root.style.setProperty( + '--lightbox-target-left-position', + targetLeft + 'px' + ); + root.style.setProperty( + '--lightbox-target-top-position', + targetTop + 'px' + ); +} diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index 6de2bdd6898596..563a91ad340b31 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -204,7 +204,6 @@ justify-content: center; align-items: center; flex-direction: column; - padding: 30px; figcaption { display: none; @@ -231,24 +230,69 @@ opacity: 0.9; } - &.initialized { - animation: both turn-off-visibility 300ms; + &.fade { + &.active { + visibility: visible; + animation: both turn-on-visibility 0.25s; + img { + animation: both turn-on-visibility 0.3s; + } + } + &.hideanimationenabled { + &:not(.active) { + animation: both turn-off-visibility 0.3s; + + img { + animation: both turn-off-visibility 0.25s; + } + } + } + } + + &.zoom { img { - animation: both turn-off-visibility 250ms; + position: absolute; + transform-origin: top left; + width: var(--lightbox-image-max-width); + height: var(--lightbox-image-max-height); } &.active { + opacity: 1; visibility: visible; - animation: both turn-on-visibility 250ms; + .wp-block-image img { + animation: lightbox-zoom-in 0.4s forwards; - img { - animation: both turn-on-visibility 300ms; + @media (prefers-reduced-motion) { + animation: both turn-on-visibility 0.4s; + } + } + .scrim { + animation: turn-on-visibility 0.4s forwards; + } + } + &.hideanimationenabled { + &:not(.active) { + .wp-block-image img { + animation: lightbox-zoom-out 0.4s forwards; + + @media (prefers-reduced-motion) { + animation: both turn-off-visibility 0.4s; + } + } + .scrim { + animation: turn-off-visibility 0.4s forwards; + } } } } } +html.has-lightbox-open { + overflow: hidden; +} + @keyframes turn-on-visibility { 0% { opacity: 0; @@ -273,6 +317,32 @@ } } -html.has-lightbox-open { - overflow: hidden; +@keyframes lightbox-zoom-in { + 0% { + left: var(--lightbox-initial-left-position); + top: var(--lightbox-initial-top-position); + transform: scale(var(--lightbox-scale-width), var(--lightbox-scale-height)); + } + 100% { + left: var(--lightbox-target-left-position); + top: var(--lightbox-target-top-position); + transform: scale(1, 1); + } +} + +@keyframes lightbox-zoom-out { + 0% { + visibility: visible; + left: var(--lightbox-target-left-position); + top: var(--lightbox-target-top-position); + transform: scale(1, 1); + } + 99% { + visibility: visible; + } + 100% { + left: var(--lightbox-initial-left-position); + top: var(--lightbox-initial-top-position); + transform: scale(var(--lightbox-scale-width), var(--lightbox-scale-height)); + } } diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index 2516a6d9c5ceaa..ad07a9b3914fd7 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -811,7 +811,12 @@ test.describe( 'Image - interactivity', () => { let blocks = await editor.getBlocks(); expect( blocks[ 0 ].attributes ).toMatchObject( { - behaviors: { lightbox: true }, + behaviors: { + lightbox: { + animation: 'zoom', + enabled: true, + }, + }, linkDestination: 'none', } ); expect( blocks[ 0 ].attributes.url ).toContain( filename ); @@ -819,7 +824,12 @@ test.describe( 'Image - interactivity', () => { await page.getByLabel( 'Behaviors' ).selectOption( '' ); blocks = await editor.getBlocks(); expect( blocks[ 0 ].attributes ).toMatchObject( { - behaviors: { lightbox: false }, + behaviors: { + lightbox: { + animation: '', + enabled: false, + }, + }, linkDestination: 'none', } ); expect( blocks[ 0 ].attributes.url ).toContain( filename ); diff --git a/test/e2e/specs/editor/various/behaviors.spec.js b/test/e2e/specs/editor/various/behaviors.spec.js index dc03dd166b001e..9fe1fedf175c3c 100644 --- a/test/e2e/specs/editor/various/behaviors.spec.js +++ b/test/e2e/specs/editor/various/behaviors.spec.js @@ -49,43 +49,6 @@ test.describe( 'Testing behaviors functionality', () => { await page.waitForLoadState(); } ); - test( '`No Behaviors` should be the default as defined in the core theme.json', async ( { - admin, - editor, - requestUtils, - page, - behaviorUtils, - } ) => { - await requestUtils.activateTheme( 'twentytwentyone' ); - await admin.createNewPost(); - const media = await behaviorUtils.createMedia(); - await editor.insertBlock( { - name: 'core/image', - attributes: { - alt: filename, - id: media.id, - url: media.source_url, - }, - } ); - - await editor.openDocumentSettingsSidebar(); - const editorSettings = page.getByRole( 'region', { - name: 'Editor settings', - } ); - await editorSettings - .getByRole( 'button', { name: 'Advanced' } ) - .click(); - const select = editorSettings.getByRole( 'combobox', { - name: 'Behavior', - } ); - - // By default, no behaviors should be selected. - await expect( select ).toHaveValue( '' ); - - // By default, you should be able to select the Lightbox behavior. - await expect( select.getByRole( 'option' ) ).toHaveCount( 2 ); - } ); - test( 'Behaviors UI can be disabled in the `theme.json`', async ( { admin, editor, @@ -143,7 +106,12 @@ test.describe( 'Testing behaviors functionality', () => { id: media.id, url: media.source_url, // Explicitly set the value for behaviors to true. - behaviors: { lightbox: true }, + behaviors: { + lightbox: { + enabled: true, + animation: 'zoom', + }, + }, }, } ); @@ -162,8 +130,8 @@ test.describe( 'Testing behaviors functionality', () => { // attributes takes precedence over the theme's value. await expect( select ).toHaveValue( 'lightbox' ); - // There should be 2 options available: `No behaviors` and `Lightbox`. - await expect( select.getByRole( 'option' ) ).toHaveCount( 2 ); + // There should be 3 options available: `No behaviors` and `Lightbox`. + await expect( select.getByRole( 'option' ) ).toHaveCount( 3 ); // We can change the value of the behaviors dropdown to `No behaviors`. await select.selectOption( { label: 'No behaviors' } ); @@ -173,50 +141,6 @@ test.describe( 'Testing behaviors functionality', () => { // lightbox even though the theme.json has it set to false. } ); - test( 'You can set the default value for the behaviors in the theme.json', async ( { - admin, - editor, - requestUtils, - page, - behaviorUtils, - } ) => { - // In this theme, the default value for settings.behaviors.blocks.core/image.lightbox is `true`. - await requestUtils.activateTheme( 'behaviors-enabled' ); - await admin.createNewPost(); - const media = await behaviorUtils.createMedia(); - - await editor.insertBlock( { - name: 'core/image', - attributes: { - alt: filename, - id: media.id, - url: media.source_url, - }, - } ); - - await editor.openDocumentSettingsSidebar(); - const editorSettings = page.getByRole( 'region', { - name: 'Editor settings', - } ); - await editorSettings - .getByRole( 'button', { name: 'Advanced' } ) - .click(); - const select = editorSettings.getByRole( 'combobox', { - name: 'Behavior', - } ); - - // The behaviors dropdown should be present and the value should be set to - // `lightbox`. - await expect( select ).toHaveValue( 'lightbox' ); - - // There should be 2 options available: `No behaviors` and `Lightbox`. - await expect( select.getByRole( 'option' ) ).toHaveCount( 2 ); - - // We can change the value of the behaviors dropdown to `No behaviors`. - await select.selectOption( { label: 'No behaviors' } ); - await expect( select ).toHaveValue( '' ); - } ); - test( 'Lightbox behavior is disabled if the Image has a link', async ( { admin, editor, @@ -254,7 +178,7 @@ test.describe( 'Testing behaviors functionality', () => { await expect( select ).toBeDisabled(); } ); - test( 'Lightbox behavior control has a Reset button that removes the markup', async ( { + test( 'Lightbox behavior control has a default option that removes the markup', async ( { admin, editor, requestUtils, @@ -293,13 +217,11 @@ test.describe( 'Testing behaviors functionality', () => { .last() .click(); - const resetButton = editorSettings.getByRole( 'button', { - name: 'Reset', + const select = editorSettings.getByRole( 'combobox', { + name: 'Behavior', } ); - expect( resetButton ).toBeDefined(); - - await resetButton.last().click(); + await select.selectOption( { label: 'Default' } ); expect( await editor.getEditedPostContent() ) .toBe( `
1024x768_e2e_test_image_size.jpeg
diff --git a/test/gutenberg-test-themes/behaviors-enabled/theme.json b/test/gutenberg-test-themes/behaviors-enabled/theme.json index f49129622d9f6d..8e7ce39023fd35 100644 --- a/test/gutenberg-test-themes/behaviors-enabled/theme.json +++ b/test/gutenberg-test-themes/behaviors-enabled/theme.json @@ -3,7 +3,10 @@ "behaviors": { "blocks": { "core/image": { - "lightbox": true + "lightbox": { + "enabled": true, + "animation": "zoom" + } } } } diff --git a/test/gutenberg-test-themes/behaviors-ui-disabled/theme.json b/test/gutenberg-test-themes/behaviors-ui-disabled/theme.json index a9f920f6dd0abc..cc4b0882fd22c3 100644 --- a/test/gutenberg-test-themes/behaviors-ui-disabled/theme.json +++ b/test/gutenberg-test-themes/behaviors-ui-disabled/theme.json @@ -3,9 +3,7 @@ "settings": { "blocks": { "core/image": { - "behaviors": { - "lightbox": false - } + "behaviors": false } } } From 45e47b118705370a5bcdcf345ba3d11016fd3ce4 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Thu, 15 Jun 2023 12:21:49 -0400 Subject: [PATCH 037/163] Change "Copy block" to "Copy" (#51532) --- .../block-mobile-toolbar/block-actions-menu.native.js | 2 +- .../block-mobile-toolbar/test/block-actions-menu.native.js | 4 ++-- .../components/block-settings-menu/block-settings-dropdown.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js index 08b5672738a2b7..e896d6054a8ff0 100644 --- a/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js +++ b/packages/block-editor/src/components/block-mobile-toolbar/block-actions-menu.native.js @@ -156,7 +156,7 @@ const BlockActionsMenu = ( { }, copyButton: { id: 'copyButtonOption', - label: __( 'Copy block' ), + label: __( 'Copy' ), value: 'copyButtonOption', onSelect: () => { const serializedBlock = serialize( diff --git a/packages/block-editor/src/components/block-mobile-toolbar/test/block-actions-menu.native.js b/packages/block-editor/src/components/block-mobile-toolbar/test/block-actions-menu.native.js index 675b286502a1f0..940d95d2749423 100644 --- a/packages/block-editor/src/components/block-mobile-toolbar/test/block-actions-menu.native.js +++ b/packages/block-editor/src/components/block-mobile-toolbar/test/block-actions-menu.native.js @@ -246,7 +246,7 @@ describe( 'Block Actions Menu', () => { fireEvent.press( getByLabelText( /Open Block Actions Menu/ ) ); // Tap on the Copy button - fireEvent.press( getByLabelText( /Copy block/ ) ); + fireEvent.press( getByLabelText( /Copy/ ) ); // Get Paragraph block paragraphBlock = await getBlock( screen, 'Paragraph' ); @@ -293,7 +293,7 @@ describe( 'Block Actions Menu', () => { fireEvent.press( getByLabelText( /Open Block Actions Menu/ ) ); // Tap on the Copy button - fireEvent.press( getByLabelText( /Copy block/ ) ); + fireEvent.press( getByLabelText( /Copy/ ) ); // Get Paragraph block paragraphBlock = await getBlock( screen, 'Paragraph' ); diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js index 2f16210822be59..9c0f81ae3a22b2 100644 --- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js +++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js @@ -41,7 +41,7 @@ const POPOVER_PROPS = { function CopyMenuItem( { blocks, onCopy, label } ) { const ref = useCopyToClipboard( () => serialize( blocks ), onCopy ); const copyMenuItemBlocksLabel = - blocks.length > 1 ? __( 'Copy blocks' ) : __( 'Copy block' ); + blocks.length > 1 ? __( 'Copy blocks' ) : __( 'Copy' ); const copyMenuItemLabel = label ? label : copyMenuItemBlocksLabel; return { copyMenuItemLabel }; } From e36186c81b894fddc01420b19b0eef0a2e678ea8 Mon Sep 17 00:00:00 2001 From: Jason Crist Date: Thu, 15 Jun 2023 15:15:10 -0400 Subject: [PATCH 038/163] Limit Copy/Paste Styles menu item to only those blocks that have either 'color' or 'typography' supports (#51503) --- .../src/components/block-actions/index.js | 10 ++++++++ .../block-settings-dropdown.js | 23 +++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/block-actions/index.js b/packages/block-editor/src/components/block-actions/index.js index a7fe38245e5ac5..b672bee63fc5b1 100644 --- a/packages/block-editor/src/components/block-actions/index.js +++ b/packages/block-editor/src/components/block-actions/index.js @@ -32,6 +32,15 @@ export default function BlockActions( { const blocks = getBlocksByClientId( clientIds ); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + + const canCopyStyles = blocks.every( ( block ) => { + return ( + !! block && + ( hasBlockSupport( block.name, 'color' ) || + hasBlockSupport( block.name, 'typography' ) ) + ); + } ); + const canDuplicate = blocks.every( ( block ) => { return ( !! block && @@ -64,6 +73,7 @@ export default function BlockActions( { const pasteStyles = usePasteStyles(); return children( { + canCopyStyles, canDuplicate, canInsertDefaultBlock, canMove, diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js index 9c0f81ae3a22b2..fe3ae0e8bc412a 100644 --- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js +++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js @@ -189,6 +189,7 @@ export function BlockSettingsDropdown( { __experimentalUpdateSelection={ ! __experimentalSelectBlock } > { ( { + canCopyStyles, canDuplicate, canInsertDefaultBlock, canMove, @@ -330,16 +331,18 @@ export function BlockSettingsDropdown( { ) } - - - - { __( 'Paste styles' ) } - - + { canCopyStyles && ( + + + + { __( 'Paste styles' ) } + + + ) } Date: Thu, 15 Jun 2023 14:43:19 -0500 Subject: [PATCH 039/163] Add missing tests for image block (#51305) * Add missing tests for image block * Add test for image link * Clean up tests -Reorganize tests to avoid unnecessary code execution -Add comments -Remove extraneous code * Remove commented code * Add clarifying comment * Improve syntax for test --- test/e2e/specs/editor/blocks/image.spec.js | 429 +++++++++++++++------ 1 file changed, 304 insertions(+), 125 deletions(-) diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index ad07a9b3914fd7..3905fc12162863 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -32,7 +32,10 @@ test.describe( 'Image', () => { await requestUtils.deleteAllMedia(); } ); - test( 'can be inserted', async ( { editor, imageBlockUtils } ) => { + test( 'can be inserted via image upload', async ( { + editor, + imageBlockUtils, + } ) => { await editor.insertBlock( { name: 'core/image' } ); const imageBlock = editor.canvas.locator( @@ -704,7 +707,7 @@ test.describe( 'Image', () => { ); } ); - test( 'should appear in the frontend published post content', async ( { + test( 'image inserted via upload should appear in the frontend published post content', async ( { editor, imageBlockUtils, page, @@ -739,11 +742,92 @@ test.describe( 'Image', () => { new RegExp( filename ) ); } ); + + test( 'image inserted via link should appear in the frontend published post content', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/image' } ); + const imageBlock = editor.canvas.locator( + 'role=document[name="Block: Image"i]' + ); + await expect( imageBlock ).toBeVisible(); + + await imageBlock + .getByRole( 'button' ) + .filter( { hasText: 'Insert from URL' } ) + .click(); + + // This form lacks distinguishing qualities other than the + // class name, so we use page.locator() instead of page.getByRole() + const form = page.locator( + 'form.block-editor-media-placeholder__url-input-form' + ); + + const imgUrl = + 'https://wp20.wordpress.net/wp-content/themes/twentyseventeen-wp20/images/wp20-logo-white.svg'; + + await form.getByLabel( 'URL' ).fill( imgUrl ); + await form.getByRole( 'button', { name: 'Apply' } ).click(); + + const imageInEditor = imageBlock.locator( 'role=img' ); + await expect( imageInEditor ).toBeVisible(); + await expect( imageInEditor ).toHaveAttribute( 'src', imgUrl ); + + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); + + const figureDom = page.getByRole( 'figure' ); + await expect( figureDom ).toBeVisible(); + + const imageDom = figureDom.locator( 'img' ); + await expect( imageDom ).toBeVisible(); + await expect( imageDom ).toHaveAttribute( 'src', imgUrl ); + } ); + + test( 'adding a link should reflect configuration in published post content', async ( { + editor, + page, + imageBlockUtils, + } ) => { + await editor.insertBlock( { name: 'core/image' } ); + const imageBlock = editor.canvas.locator( + 'role=document[name="Block: Image"i]' + ); + await expect( imageBlock ).toBeVisible(); + + await imageBlockUtils.upload( + imageBlock.locator( 'data-testid=form-file-upload-input' ) + ); + + await page + .getByLabel( 'Block tools' ) + .getByLabel( 'Insert link' ) + .click(); + + // This form lacks distinguishing qualities other than the + // class name, so we use page.locator() instead of page.getByRole() + const form = page.locator( '.block-editor-url-popover__link-editor' ); + + const url = 'https://wordpress.org'; + + await form.getByLabel( 'URL' ).fill( url ); + + await form.getByRole( 'button', { name: 'Apply' } ).click(); + + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); + + const figureDom = page.getByRole( 'figure' ); + await expect( figureDom ).toBeVisible(); + + const linkDom = figureDom.locator( 'a' ); + await expect( linkDom ).toBeVisible(); + await expect( linkDom ).toHaveAttribute( 'href', url ); + } ); } ); test.describe( 'Image - interactivity', () => { - let filename = null; - test.beforeAll( async ( { requestUtils } ) => { await requestUtils.deleteAllMedia(); } ); @@ -752,7 +836,7 @@ test.describe( 'Image - interactivity', () => { await requestUtils.deleteAllMedia(); } ); - test.beforeEach( async ( { admin, page, editor, imageBlockUtils } ) => { + test.beforeEach( async ( { admin, page, editor } ) => { await admin.visitAdminPage( '/admin.php', 'page=gutenberg-experiments' @@ -767,20 +851,6 @@ test.describe( 'Image - interactivity', () => { await admin.createNewPost(); await editor.insertBlock( { name: 'core/image' } ); - - const imageBlock = editor.canvas.locator( - 'role=document[name="Block: Image"i]' - ); - await expect( imageBlock ).toBeVisible(); - - filename = await imageBlockUtils.upload( - imageBlock.locator( 'data-testid=form-file-upload-input' ) - ); - const image = imageBlock.locator( 'role=img' ); - await expect( image ).toBeVisible(); - await expect( image ).toHaveAttribute( 'src', new RegExp( filename ) ); - - await editor.openDocumentSettingsSidebar(); } ); test.afterEach( async ( { requestUtils, admin, page } ) => { @@ -800,142 +870,251 @@ test.describe( 'Image - interactivity', () => { await page.waitForLoadState(); } ); - test( 'should toggle "lightbox" in saved attributes', async ( { - editor, - page, - } ) => { - await page.getByRole( 'button', { name: 'Advanced' } ).click(); - await page - .getByRole( 'combobox', { name: 'Behaviors' } ) - .selectOption( 'lightbox' ); + test.describe( 'tests using uploaded image', () => { + let filename = null; - let blocks = await editor.getBlocks(); - expect( blocks[ 0 ].attributes ).toMatchObject( { - behaviors: { - lightbox: { - animation: 'zoom', - enabled: true, - }, - }, - linkDestination: 'none', + test.beforeEach( async ( { editor, imageBlockUtils } ) => { + const imageBlock = editor.canvas.locator( + 'role=document[name="Block: Image"i]' + ); + await expect( imageBlock ).toBeVisible(); + + filename = await imageBlockUtils.upload( + imageBlock.locator( 'data-testid=form-file-upload-input' ) + ); + const image = imageBlock.locator( 'role=img' ); + await expect( image ).toBeVisible(); + await expect( image ).toHaveAttribute( + 'src', + new RegExp( filename ) + ); + + await editor.openDocumentSettingsSidebar(); } ); - expect( blocks[ 0 ].attributes.url ).toContain( filename ); - - await page.getByLabel( 'Behaviors' ).selectOption( '' ); - blocks = await editor.getBlocks(); - expect( blocks[ 0 ].attributes ).toMatchObject( { - behaviors: { - lightbox: { - animation: '', - enabled: false, + + test( 'should toggle "lightbox" in saved attributes', async ( { + editor, + page, + } ) => { + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + await page + .getByRole( 'combobox', { name: 'Behaviors' } ) + .selectOption( 'lightbox' ); + + let blocks = await editor.getBlocks(); + expect( blocks[ 0 ].attributes ).toMatchObject( { + behaviors: { + lightbox: { + animation: 'zoom', + enabled: true, + }, }, - }, - linkDestination: 'none', + linkDestination: 'none', + } ); + expect( blocks[ 0 ].attributes.url ).toContain( filename ); + + await page.getByLabel( 'Behaviors' ).selectOption( '' ); + blocks = await editor.getBlocks(); + expect( blocks[ 0 ].attributes ).toMatchObject( { + behaviors: { + lightbox: { + animation: '', + enabled: false, + }, + }, + linkDestination: 'none', + } ); + expect( blocks[ 0 ].attributes.url ).toContain( filename ); } ); - expect( blocks[ 0 ].attributes.url ).toContain( filename ); - } ); - test( 'should open and close the image in a lightbox using the mouse', async ( { - editor, - page, - } ) => { - await page.getByRole( 'button', { name: 'Advanced' } ).click(); - await page - .getByRole( 'combobox', { name: 'Behaviors' } ) - .selectOption( 'lightbox' ); + test( 'should open and close the image in a lightbox when using a mouse and dynamically load src', async ( { + editor, + page, + } ) => { + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + await page + .getByRole( 'combobox', { name: 'Behaviors' } ) + .selectOption( 'lightbox' ); - const postId = await editor.publishPost(); - await page.goto( `/?p=${ postId }` ); + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); - const lightbox = page.locator( '.wp-lightbox-overlay' ); - await expect( lightbox ).toBeHidden(); + const lightbox = page.locator( '.wp-lightbox-overlay' ); + await expect( lightbox ).toBeHidden(); + const image = lightbox.locator( 'img' ); - await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); + await expect( image ).toHaveAttribute( 'src', '' ); - const image = lightbox.locator( 'img' ); - await expect( image ).toHaveAttribute( 'src', new RegExp( filename ) ); + await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); - await expect( lightbox ).toBeVisible(); + await expect( image ).toHaveAttribute( + 'src', + new RegExp( filename ) + ); - const closeButton = lightbox.getByRole( 'button', { - name: 'Close', - } ); - await closeButton.click(); + await expect( lightbox ).toBeVisible(); - await expect( lightbox ).toBeHidden(); - } ); + const closeButton = lightbox.getByRole( 'button', { + name: 'Close', + } ); + await closeButton.click(); - test.describe( 'keyboard navigation', () => { - let openLightboxButton; - let lightbox; - let closeButton; + await expect( lightbox ).toBeHidden(); + } ); - test.beforeEach( async ( { page, editor } ) => { + test( 'lightbox should be overriden when link is configured for image', async ( { + editor, + page, + } ) => { await page.getByRole( 'button', { name: 'Advanced' } ).click(); + const behaviorSelect = page.getByRole( 'combobox', { + name: 'Behaviors', + } ); + await behaviorSelect.selectOption( 'lightbox' ); + await page - .getByRole( 'combobox', { name: 'Behaviors' } ) - .selectOption( 'lightbox' ); + .getByLabel( 'Block tools' ) + .getByLabel( 'Insert link' ) + .click(); + + const form = page.locator( + '.block-editor-url-popover__link-editor' + ); + + const url = 'https://wordpress.org'; + + await form.getByLabel( 'URL' ).fill( url ); + + await form.getByRole( 'button', { name: 'Apply' } ).click(); + await expect( behaviorSelect ).toBeDisabled(); const postId = await editor.publishPost(); await page.goto( `/?p=${ postId }` ); - openLightboxButton = page.getByRole( 'button', { - name: 'Enlarge image', + // The lightbox markup should not appear in the DOM at all + await expect( + page.getByRole( 'button', { name: 'Enlarge image' } ) + ).not.toBeInViewport(); + } ); + + test.describe( 'keyboard navigation', () => { + let openLightboxButton; + let lightbox; + let closeButton; + + test.beforeEach( async ( { page, editor } ) => { + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + await page + .getByRole( 'combobox', { name: 'Behaviors' } ) + .selectOption( 'lightbox' ); + + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); + + openLightboxButton = page.getByRole( 'button', { + name: 'Enlarge image', + } ); + lightbox = page.getByRole( 'dialog' ); + closeButton = lightbox.getByRole( 'button', { + name: 'Close', + } ); } ); - lightbox = page.getByRole( 'dialog' ); - closeButton = lightbox.getByRole( 'button', { - name: 'Close', + + test( 'should open and focus appropriately using enter key', async ( { + page, + } ) => { + // Open and close lightbox using the close button + await openLightboxButton.focus(); + await page.keyboard.press( 'Enter' ); + await expect( lightbox ).toBeVisible(); + await expect( closeButton ).toBeFocused(); } ); - } ); - test( 'should open and focus appropriately using enter key', async ( { - page, - } ) => { - // Open and close lightbox using the close button - await openLightboxButton.focus(); - await page.keyboard.press( 'Enter' ); - await expect( lightbox ).toBeVisible(); - await expect( closeButton ).toBeFocused(); - } ); + test( 'should close and focus appropriately using enter key on close button', async ( { + page, + } ) => { + // Open and close lightbox using the close button + await openLightboxButton.focus(); + await page.keyboard.press( 'Enter' ); + await expect( lightbox ).toBeVisible(); + await expect( closeButton ).toBeFocused(); + await page.keyboard.press( 'Enter' ); + await expect( lightbox ).toBeHidden(); + await expect( openLightboxButton ).toBeFocused(); + } ); - test( 'should close and focus appropriately using enter key on close button', async ( { - page, - } ) => { - // Open and close lightbox using the close button - await openLightboxButton.focus(); - await page.keyboard.press( 'Enter' ); - await expect( lightbox ).toBeVisible(); - await expect( closeButton ).toBeFocused(); - await page.keyboard.press( 'Enter' ); - await expect( lightbox ).toBeHidden(); - await expect( openLightboxButton ).toBeFocused(); - } ); + test( 'should close and focus appropriately using escape key', async ( { + page, + } ) => { + await openLightboxButton.focus(); + await page.keyboard.press( 'Enter' ); + await expect( lightbox ).toBeVisible(); + await expect( closeButton ).toBeFocused(); + await page.keyboard.press( 'Escape' ); + await expect( lightbox ).toBeHidden(); + await expect( openLightboxButton ).toBeFocused(); + } ); - test( 'should close and focus appropriately using escape key', async ( { - page, - } ) => { - await openLightboxButton.focus(); - await page.keyboard.press( 'Enter' ); - await expect( lightbox ).toBeVisible(); - await expect( closeButton ).toBeFocused(); - await page.keyboard.press( 'Escape' ); - await expect( lightbox ).toBeHidden(); - await expect( openLightboxButton ).toBeFocused(); + // TO DO: Add these tests, which will involve adding a caption + // to uploaded test images + // test( 'should trap focus appropriately when using tab', async ( { + // page, + // } ) => { + + // } ); + + // test( 'should trap focus appropriately using shift+tab', async ( { + // page, + // } ) => { + + // } ); } ); + } ); + + test( 'lightbox should work as expected when inserting image from URL', async ( { + editor, + page, + } ) => { + const imageBlockFromUrl = editor.canvas.locator( + 'role=document[name="Block: Image"i]' + ); + await expect( imageBlockFromUrl ).toBeVisible(); + + await imageBlockFromUrl + .getByRole( 'button' ) + .filter( { hasText: 'Insert from URL' } ) + .click(); + + const form = page.locator( + '.block-editor-media-placeholder__url-input-form' + ); + + const imgUrl = + 'https://wp20.wordpress.net/wp-content/themes/twentyseventeen-wp20/images/wp20-logo-white.svg'; + + await form.getByLabel( 'URL' ).fill( imgUrl ); - // TO DO: Add these tests, which will involve adding a caption - // to uploaded test images - // test( 'should trap focus appropriately when using tab', async ( { - // page, - // } ) => { + await form.getByRole( 'button', { name: 'Apply' } ).click(); - // } ); + const image = imageBlockFromUrl.locator( 'role=img' ); + await expect( image ).toBeVisible(); + await expect( image ).toHaveAttribute( 'src', imgUrl ); + + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + await page + .getByRole( 'combobox', { name: 'Behaviors' } ) + .selectOption( 'lightbox' ); - // test( 'should trap focus appropriately using shift+tab', async ( { - // page, - // } ) => { + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); + + const lightbox = page.locator( '.wp-lightbox-overlay' ); + const imageDom = lightbox.locator( 'img' ); + await expect( imageDom ).toHaveAttribute( 'src', '' ); + + await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); - // } ); + await expect( imageDom ).toHaveAttribute( 'src', imgUrl ); } ); } ); From bb9ce526f52faa2aa743791c2ccd2e60810bc47e Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 15 Jun 2023 21:21:33 +0100 Subject: [PATCH 040/163] Add commands to access template, template parts and styles (#51501) * Add commands to access template, template parts and styles * Add more navigation commands * Fix icons --- .../src/site-editor-navigation-commands.js | 122 +++++++++++++++++- 1 file changed, 118 insertions(+), 4 deletions(-) diff --git a/packages/core-commands/src/site-editor-navigation-commands.js b/packages/core-commands/src/site-editor-navigation-commands.js index a787183a0a6100..f516bcec310632 100644 --- a/packages/core-commands/src/site-editor-navigation-commands.js +++ b/packages/core-commands/src/site-editor-navigation-commands.js @@ -6,7 +6,14 @@ import { __ } from '@wordpress/i18n'; import { useMemo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; -import { post, page, layout, symbolFilled } from '@wordpress/icons'; +import { + post, + page, + layout, + symbolFilled, + styles, + navigation, +} from '@wordpress/icons'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { getQueryArg, addQueryArgs, getPath } from '@wordpress/url'; @@ -55,9 +62,6 @@ const getNavigationCommandLoaderPerPostType = ( postType ) => 'getEntityRecords', [ 'postType', postType, query ] ), - // We're using the string literal to check whether we're in the site editor. - /* eslint-disable-next-line @wordpress/data-no-store-string-literals */ - isSiteEditor: !! select( 'edit-site' ), }; }, [ supportsSearch, search ] @@ -114,6 +118,111 @@ const useTemplateNavigationCommandLoader = const useTemplatePartNavigationCommandLoader = getNavigationCommandLoaderPerPostType( 'wp_template_part' ); +function useSiteEditorBasicNavigationCommands() { + const history = useHistory(); + const isSiteEditor = getPath( window.location.href )?.includes( + 'site-editor.php' + ); + const commands = useMemo( () => { + const result = []; + result.push( { + name: 'core/edit-site/open-navigation', + label: __( 'Open navigation' ), + icon: navigation, + callback: ( { close } ) => { + const args = { + path: '/navigation', + }; + const targetUrl = addQueryArgs( 'site-editor.php', args ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); + + result.push( { + name: 'core/edit-site/open-pages', + label: __( 'Open pages' ), + icon: page, + callback: ( { close } ) => { + const args = { + path: '/page', + }; + const targetUrl = addQueryArgs( 'site-editor.php', args ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); + + result.push( { + name: 'core/edit-site/open-styles', + label: __( 'Open style variations' ), + icon: styles, + callback: ( { close } ) => { + const args = { + path: '/wp_global_styles', + }; + const targetUrl = addQueryArgs( 'site-editor.php', args ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); + + result.push( { + name: 'core/edit-site/open-templates', + label: __( 'Open templates' ), + icon: layout, + callback: ( { close } ) => { + const args = { + path: '/wp_template', + }; + const targetUrl = addQueryArgs( 'site-editor.php', args ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); + + result.push( { + name: 'core/edit-site/open-template-parts', + label: __( 'Open library' ), + icon: symbolFilled, + callback: ( { close } ) => { + const args = { + path: '/wp_template_part', + }; + const targetUrl = addQueryArgs( 'site-editor.php', args ); + if ( isSiteEditor ) { + history.push( args ); + } else { + document.location = targetUrl; + } + close(); + }, + } ); + return result; + }, [ history, isSiteEditor ] ); + + return { + commands, + isLoading: false, + }; +} + export function useSiteEditorNavigationCommands() { useCommandLoader( { name: 'core/edit-site/navigate-pages', @@ -131,4 +240,9 @@ export function useSiteEditorNavigationCommands() { name: 'core/edit-site/navigate-template-parts', hook: useTemplatePartNavigationCommandLoader, } ); + useCommandLoader( { + name: 'core/edit-site/basic-navigation', + hook: useSiteEditorBasicNavigationCommands, + context: 'site-editor', + } ); } From 8f9ab3889205b0b0dafced1b968cfa8f1eec6405 Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Fri, 16 Jun 2023 10:40:11 +1000 Subject: [PATCH 041/163] [RNMobile] Fix crash when using the delete key to remove a single button (#51435) * Fix crash when using the delete key to remove a single button * Add onDeleteBlock prop to block.native.js * Update button edit.native.js snapshots * Update button delete test to focus button input * Update button test to use triggerBlockListLayout --- .../src/components/block-list/block.native.js | 1 + .../test/__snapshots__/edit.native.js.snap | 6 +++++ .../src/buttons/test/edit.native.js | 27 +++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index 41e166327ccca2..bb537f22d0fb06 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -345,6 +345,7 @@ function BlockListBlock( { isSelectionEnabled={ isSelectionEnabled } mergeBlocks={ canRemove ? onMerge : undefined } name={ name } + onDeleteBlock={ onDeleteBlock } onFocus={ onFocus } onRemove={ canRemove ? onRemove : undefined } onReplace={ canRemove ? onReplace : undefined } diff --git a/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap index 1a55c807225d9d..25867634d12d8e 100644 --- a/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap @@ -71,3 +71,9 @@ exports[`Buttons block when a button is shown removing button along with buttons

" `; + +exports[`Buttons block when a button is shown removing button along with buttons block removes the button and buttons block when deleting the block using the delete (backspace) key 1`] = ` +" +

+" +`; diff --git a/packages/block-library/src/buttons/test/edit.native.js b/packages/block-library/src/buttons/test/edit.native.js index 5ba97e7bb4caf3..ade014330a1b29 100644 --- a/packages/block-library/src/buttons/test/edit.native.js +++ b/packages/block-library/src/buttons/test/edit.native.js @@ -18,6 +18,7 @@ import { */ import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks'; import { registerCoreBlocks } from '@wordpress/block-library'; +import { BACKSPACE } from '@wordpress/keycodes'; const BUTTONS_HTML = `
@@ -232,6 +233,32 @@ describe( 'Buttons block', () => { expect( getEditorHtml() ).toMatchSnapshot(); } ); + + it( 'removes the button and buttons block when deleting the block using the delete (backspace) key', async () => { + const screen = await initializeEditor( { + initialHtml: BUTTONS_HTML, + } ); + + // Get block + const buttonsBlock = await getBlock( screen, 'Buttons' ); + triggerBlockListLayout( buttonsBlock ); + + // Get inner button block + const buttonBlock = await getBlock( screen, 'Button' ); + fireEvent.press( buttonBlock ); + + const buttonInput = + within( buttonBlock ).getByLabelText( 'Text input. Empty' ); + + // Delete block + fireEvent( buttonInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: BACKSPACE, + } ); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); } ); From d481efbb7c29ae118c2e9d316861946f58ef8d28 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Fri, 16 Jun 2023 15:10:08 +1200 Subject: [PATCH 042/163] List block: Add numbering type selection (#51186) Co-authored-by: Alex Stine Co-authored-by: Ramon --- .../src/list/ordered-list-settings.js | 22 +++++++++++++++++-- .../blocks/core__list__ol-with-type.html | 7 ++++++ .../blocks/core__list__ol-with-type.json | 21 ++++++++++++++++++ .../core__list__ol-with-type.parsed.json | 20 +++++++++++++++++ .../core__list__ol-with-type.serialized.html | 5 +++++ 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 test/integration/fixtures/blocks/core__list__ol-with-type.html create mode 100644 test/integration/fixtures/blocks/core__list__ol-with-type.json create mode 100644 test/integration/fixtures/blocks/core__list__ol-with-type.parsed.json create mode 100644 test/integration/fixtures/blocks/core__list__ol-with-type.serialized.html diff --git a/packages/block-library/src/list/ordered-list-settings.js b/packages/block-library/src/list/ordered-list-settings.js index 7fd51a5c7e576e..60e7e33d0ad51f 100644 --- a/packages/block-library/src/list/ordered-list-settings.js +++ b/packages/block-library/src/list/ordered-list-settings.js @@ -3,9 +3,14 @@ */ import { __ } from '@wordpress/i18n'; import { InspectorControls } from '@wordpress/block-editor'; -import { TextControl, PanelBody, ToggleControl } from '@wordpress/components'; +import { + TextControl, + PanelBody, + ToggleControl, + SelectControl, +} from '@wordpress/components'; -const OrderedListSettings = ( { setAttributes, reversed, start } ) => ( +const OrderedListSettings = ( { setAttributes, reversed, start, type } ) => ( ( value={ Number.isInteger( start ) ? start.toString( 10 ) : '' } step="1" /> + setAttributes( { type: newValue } ) } + /> +
    + +
  1. Item 1
  2. + +
+ diff --git a/test/integration/fixtures/blocks/core__list__ol-with-type.json b/test/integration/fixtures/blocks/core__list__ol-with-type.json new file mode 100644 index 00000000000000..a15b1546b0ed4c --- /dev/null +++ b/test/integration/fixtures/blocks/core__list__ol-with-type.json @@ -0,0 +1,21 @@ +[ + { + "name": "core/list", + "isValid": true, + "attributes": { + "ordered": true, + "values": "", + "type": "A" + }, + "innerBlocks": [ + { + "name": "core/list-item", + "isValid": true, + "attributes": { + "content": "Item 1" + }, + "innerBlocks": [] + } + ] + } +] diff --git a/test/integration/fixtures/blocks/core__list__ol-with-type.parsed.json b/test/integration/fixtures/blocks/core__list__ol-with-type.parsed.json new file mode 100644 index 00000000000000..7ddda9e08da5ab --- /dev/null +++ b/test/integration/fixtures/blocks/core__list__ol-with-type.parsed.json @@ -0,0 +1,20 @@ +[ + { + "blockName": "core/list", + "attrs": { + "ordered": true, + "type": "A" + }, + "innerBlocks": [ + { + "blockName": "core/list-item", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\t
  • Item 1
  • \n\t", + "innerContent": [ "\n\t
  • Item 1
  • \n\t" ] + } + ], + "innerHTML": "\n
      \n\t\n
    \n", + "innerContent": [ "\n
      \n\t", null, "\n
    \n" ] + } +] diff --git a/test/integration/fixtures/blocks/core__list__ol-with-type.serialized.html b/test/integration/fixtures/blocks/core__list__ol-with-type.serialized.html new file mode 100644 index 00000000000000..a785d55d92bcee --- /dev/null +++ b/test/integration/fixtures/blocks/core__list__ol-with-type.serialized.html @@ -0,0 +1,5 @@ + +
      +
    1. Item 1
    2. +
    + From b6fb0a615a8b109749668ca9fa557aa9e0ba5963 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Fri, 16 Jun 2023 13:56:56 +1000 Subject: [PATCH 043/163] Sticky Position: Try re-enabling non-root sticky position (#49321) * Sticky Position: Try re-enabling non-root sticky position, and add a visualizer to define sticky area * Fix whitespace * Ensure the position visualizer does not interfere with the ability to select inner blocks * Ensure resizing the block editor will update the block popover dimensions * Try simplifying the display logic so it always shows the visualizer if the block is selected * Small code quality tweaks * Try dotted outline, subtle display on focus and hover label * Fix help text and spacing * Update visualizer to only display on hover * Remove visualizer --- packages/block-editor/src/hooks/position.js | 36 ++++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/hooks/position.js b/packages/block-editor/src/hooks/position.js index 81f29fd6a41447..32e8dc8530d370 100644 --- a/packages/block-editor/src/hooks/position.js +++ b/packages/block-editor/src/hooks/position.js @@ -28,6 +28,7 @@ import { addFilter } from '@wordpress/hooks'; import BlockList from '../components/block-list'; import useSetting from '../components/use-setting'; import InspectorControls from '../components/inspector-controls'; +import useBlockDisplayInformation from '../components/use-block-display-information'; import { cleanEmptyObject } from './utils'; import { unlock } from '../lock-unlock'; import { store as blockEditorStore } from '../store'; @@ -222,32 +223,39 @@ export function PositionPanel( props ) { const allowSticky = hasStickyPositionSupport( blockName ); const value = style?.position?.type; - const { hasParents } = useSelect( + const { firstParentClientId } = useSelect( ( select ) => { const { getBlockParents } = select( blockEditorStore ); const parents = getBlockParents( clientId ); - return { - hasParents: parents.length, - }; + return { firstParentClientId: parents[ parents.length - 1 ] }; }, [ clientId ] ); + const blockInformation = useBlockDisplayInformation( firstParentClientId ); + const stickyHelpText = + allowSticky && value === STICKY_OPTION.value && blockInformation + ? sprintf( + /* translators: %s: the name of the parent block. */ + __( + 'The block will stick to the scrollable area of the parent %s block.' + ), + blockInformation.title + ) + : null; + const options = useMemo( () => { const availableOptions = [ DEFAULT_OPTION ]; - // Only display sticky option if the block has no parents (is at the root of the document), - // or if the block already has a sticky position value set. - if ( - ( allowSticky && ! hasParents ) || - value === STICKY_OPTION.value - ) { + // Display options if they are allowed, or if a block already has a valid value set. + // This allows for a block to be switched off from a position type that is not allowed. + if ( allowSticky || value === STICKY_OPTION.value ) { availableOptions.push( STICKY_OPTION ); } if ( allowFixed || value === FIXED_OPTION.value ) { availableOptions.push( FIXED_OPTION ); } return availableOptions; - }, [ allowFixed, allowSticky, hasParents, value ] ); + }, [ allowFixed, allowSticky, value ] ); const onChangeType = ( next ) => { // For now, use a hard-coded `0px` value for the position. @@ -281,7 +289,11 @@ export function PositionPanel( props ) { web: options.length > 1 ? ( - + Date: Fri, 16 Jun 2023 14:37:52 +1000 Subject: [PATCH 044/163] Add 'Edit template' and 'Back to page' commands (#51364) * Add 'Edit template' and 'Back to page' commands Add commands specific to editing a page in the site editor to the command centre. * Re-order template commands --- .../hooks/commands/use-edit-mode-commands.js | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js index 67953b293b2aaa..16414eb1cc98f8 100644 --- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js +++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js @@ -1,9 +1,9 @@ /** * WordPress dependencies */ -import { useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; -import { trash, backup } from '@wordpress/icons'; +import { trash, backup, layout, page } from '@wordpress/icons'; import { useCommandLoader } from '@wordpress/commands'; import { privateApis as routerPrivateApis } from '@wordpress/router'; @@ -19,16 +19,67 @@ import { unlock } from '../../lock-unlock'; const { useHistory } = unlock( routerPrivateApis ); function useEditModeCommandLoader() { - const { removeTemplate, revertTemplate } = useDispatch( editSiteStore ); - const history = useHistory(); const { isLoaded, record: template } = useEditedEntityRecord(); - const isRemovable = - isLoaded && !! template && isTemplateRemovable( template ); - const isRevertable = - isLoaded && !! template && isTemplateRevertable( template ); + const { removeTemplate, revertTemplate, setHasPageContentFocus } = + useDispatch( editSiteStore ); + const history = useHistory(); + const { isPage, hasPageContentFocus } = useSelect( + ( select ) => ( { + isPage: select( editSiteStore ).isPage(), + hasPageContentFocus: select( editSiteStore ).hasPageContentFocus(), + } ), + [] + ); + + if ( ! isLoaded ) { + return { isLoading: true, commands: [] }; + } const commands = []; - if ( isRemovable ) { + + if ( isPage ) { + if ( hasPageContentFocus ) { + commands.push( { + name: 'core/switch-to-template-focus', + label: __( 'Edit template' ), + icon: layout, + context: 'site-editor-edit', + callback: ( { close } ) => { + setHasPageContentFocus( false ); + close(); + }, + } ); + } else { + commands.push( { + name: 'core/switch-to-page-focus', + label: __( 'Back to page' ), + icon: page, + context: 'site-editor-edit', + callback: ( { close } ) => { + setHasPageContentFocus( true ); + close(); + }, + } ); + } + } + + if ( isTemplateRevertable( template ) && ! hasPageContentFocus ) { + const label = + template.type === 'wp_template' + ? __( 'Reset template' ) + : __( 'Reset template part' ); + commands.push( { + name: 'core/reset-template', + label, + icon: backup, + callback: ( { close } ) => { + revertTemplate( template ); + close(); + }, + } ); + } + + if ( isTemplateRemovable( template ) && ! hasPageContentFocus ) { const label = template.type === 'wp_template' ? __( 'Delete template' ) @@ -48,21 +99,6 @@ function useEditModeCommandLoader() { }, } ); } - if ( isRevertable ) { - const label = - template.type === 'wp_template' - ? __( 'Reset template' ) - : __( 'Reset template part' ); - commands.push( { - name: 'core/reset-template', - label, - icon: backup, - callback: ( { close } ) => { - revertTemplate( template ); - close(); - }, - } ); - } return { isLoading: ! isLoaded, From 6360e763c0b6a134a587f10a9290b60518c3292d Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:15:40 +0900 Subject: [PATCH 045/163] Color Randomizer: Fix an error when the theme has no color palette (#51539) --- packages/edit-site/src/components/global-styles/palette.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/global-styles/palette.js b/packages/edit-site/src/components/global-styles/palette.js index 0aea8554de8de2..6e9757415524cb 100644 --- a/packages/edit-site/src/components/global-styles/palette.js +++ b/packages/edit-site/src/components/global-styles/palette.js @@ -91,7 +91,7 @@ function Palette( { name } ) { - { randomizeThemeColors && ( + { themeColors?.length > 0 && (
  • diff --git a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js index 1c369703be5d72..2fcdcbb2c7e1bb 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js @@ -7,6 +7,7 @@ import { store as editorStore } from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; +import { navigation as navigationIcon } from '@wordpress/icons'; /** * Internal dependencies @@ -20,7 +21,7 @@ import SidebarCard from '../sidebar-card'; export default function TemplatePanel() { const { info: { title, description, icon }, - template, + record, } = useSelect( ( select ) => { const { getEditedPostType, getEditedPostId } = select( editSiteStore ); const { getEditedEntityRecord } = select( coreStore ); @@ -29,11 +30,11 @@ export default function TemplatePanel() { const postType = getEditedPostType(); const postId = getEditedPostId(); - const record = getEditedEntityRecord( 'postType', postType, postId ); + const _record = getEditedEntityRecord( 'postType', postType, postId ); - const info = record ? getTemplateInfo( record ) : {}; + const info = _record ? getTemplateInfo( _record ) : {}; - return { info, template: record }; + return { info, record: _record }; }, [] ); if ( ! title && ! description ) { @@ -45,9 +46,11 @@ export default function TemplatePanel() { } + actions={ } > diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/edit-button.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/edit-button.js new file mode 100644 index 00000000000000..7d084b6db4e260 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/edit-button.js @@ -0,0 +1,24 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; +import { pencil } from '@wordpress/icons'; +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; +import SidebarButton from '../sidebar-button'; +import { unlock } from '../../lock-unlock'; + +export default function EditButton() { + const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); + + return ( + setCanvasMode( 'edit' ) } + label={ __( 'Edit' ) } + icon={ pencil } + /> + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js index 576b96ab3f2969..d4027331d5cb09 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/index.js @@ -17,6 +17,7 @@ import { store as noticesStore } from '@wordpress/notices'; import { SidebarNavigationScreenWrapper } from '../sidebar-navigation-screen-navigation-menus'; import ScreenNavigationMoreMenu from './more-menu'; import NavigationMenuEditor from './navigation-menu-editor'; +import EditButton from './edit-button'; export default function SidebarNavigationScreenNavigationMenu() { const { @@ -214,17 +215,33 @@ export default function SidebarNavigationScreenNavigationMenu() { return ( + <> + + + } title={ decodeEntities( menuTitle ) } - description={ __( - 'Navigation menus are a curated collection of blocks that allow visitors to get around your site.' - ) } + description={ + <> +

    + { sprintf( + /* translators: %s: Navigation menu title */ + 'This is your "%s" navigation menu. ', + decodeEntities( menuTitle ) + ) } +

    +

    + { __( + 'You can edit this menu here, but be aware that visual styles might be applied separately in templates or template parts, so the preview shown here can be incomplete.' + ) } +

    + + } >
    diff --git a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js index 510932d7c4f370..32413a943bb9a6 100644 --- a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js +++ b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js @@ -31,7 +31,7 @@ export default function useInitEditedEntityFromURL() { }; }, [] ); - const { setTemplate, setTemplatePart, setPage } = + const { setTemplate, setTemplatePart, setPage, setNavigationMenu } = useDispatch( editSiteStore ); useEffect( () => { @@ -43,6 +43,9 @@ export default function useInitEditedEntityFromURL() { case 'wp_template_part': setTemplatePart( postId ); break; + case 'wp_navigation': + setNavigationMenu( postId ); + break; default: setPage( { context: { postType, postId }, @@ -71,5 +74,6 @@ export default function useInitEditedEntityFromURL() { setPage, setTemplate, setTemplatePart, + setNavigationMenu, ] ); } diff --git a/packages/edit-site/src/hooks/index.js b/packages/edit-site/src/hooks/index.js index 513634c55b8f01..4e871f4e3824eb 100644 --- a/packages/edit-site/src/hooks/index.js +++ b/packages/edit-site/src/hooks/index.js @@ -4,3 +4,4 @@ import './components'; import './push-changes-to-global-styles'; import './template-part-edit'; +import './navigation-menu-edit'; diff --git a/packages/edit-site/src/hooks/navigation-menu-edit.js b/packages/edit-site/src/hooks/navigation-menu-edit.js new file mode 100644 index 00000000000000..6b2c1166c9aa2f --- /dev/null +++ b/packages/edit-site/src/hooks/navigation-menu-edit.js @@ -0,0 +1,95 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { + BlockControls, + privateApis as blockEditorPrivateApis, +} from '@wordpress/block-editor'; +import { store as coreStore } from '@wordpress/core-data'; +import { ToolbarButton } from '@wordpress/components'; +import { addFilter } from '@wordpress/hooks'; +import { createHigherOrderComponent } from '@wordpress/compose'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; + +/** + * Internal dependencies + */ +import { useLink } from '../components/routes/link'; +import { unlock } from '../lock-unlock'; + +const { useLocation } = unlock( routerPrivateApis ); +const { useBlockEditingMode } = unlock( blockEditorPrivateApis ); + +function NavigationMenuEdit( { attributes } ) { + const { ref } = attributes; + const { params } = useLocation(); + const blockEditingMode = useBlockEditingMode(); + const navigationMenu = useSelect( + ( select ) => { + return select( coreStore ).getEntityRecord( + 'postType', + 'wp_navigation', + // Ideally this should be an official public API. + ref + ); + }, + [ ref ] + ); + + const linkProps = useLink( + { + postId: navigationMenu?.id, + postType: navigationMenu?.type, + canvas: 'edit', + }, + { + // this applies to Navigation Menus as well. + fromTemplateId: params.postId, + } + ); + + // A non-default setting for block editing mode indicates that the + // editor should restrict "editing" actions. Therefore the `Edit` button + // should not be displayed. + if ( ! navigationMenu || blockEditingMode !== 'default' ) { + return null; + } + + return ( + + { + linkProps.onClick( event ); + } } + > + { __( 'Edit' ) } + + + ); +} + +export const withEditBlockControls = createHigherOrderComponent( + ( BlockEdit ) => ( props ) => { + const { attributes, name } = props; + const isDisplayed = name === 'core/navigation' && attributes.ref; + + return ( + <> + + { isDisplayed && ( + + ) } + + ); + }, + 'withEditBlockControls' +); + +addFilter( + 'editor.BlockEdit', + 'core/edit-site/navigation-edit-button', + withEditBlockControls +); diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 0e4e1ff00770f5..eba997f1a6f682 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -176,6 +176,21 @@ export function setTemplatePart( templatePartId ) { }; } +/** + * Action that sets a navigation menu. + * + * @param {string} navigationMenuId The Navigation Menu Post ID. + * + * @return {Object} Action object. + */ +export function setNavigationMenu( navigationMenuId ) { + return { + type: 'SET_EDITED_POST', + postType: 'wp_navigation', + id: navigationMenuId, + }; +} + /** * @deprecated */ From a3f9fbd8130ff1e8e84afb4659991614bdda035d Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 16 Jun 2023 14:23:51 +0400 Subject: [PATCH 053/163] Code Data: Fix ESLint warning for the 'useEntityRecord' hook (#51562) --- packages/core-data/src/hooks/use-entity-record.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-data/src/hooks/use-entity-record.ts b/packages/core-data/src/hooks/use-entity-record.ts index 9734b4428e89a9..310c013ce0b877 100644 --- a/packages/core-data/src/hooks/use-entity-record.ts +++ b/packages/core-data/src/hooks/use-entity-record.ts @@ -160,7 +160,7 @@ export default function useEntityRecord< RecordType >( ...saveOptions, } ), } ), - [ recordId ] + [ editEntityRecord, kind, name, recordId, saveEditedEntityRecord ] ); const { editedRecord, hasEdits } = useSelect( From 2fa09fe953542b5f40031b6650d5825749ab2b53 Mon Sep 17 00:00:00 2001 From: Joen A <1204802+jasmussen@users.noreply.github.com> Date: Fri, 16 Jun 2023 12:35:55 +0200 Subject: [PATCH 054/163] Update: Adjust modal radius to be between frame and buttons. (#51254) * Update: Adjust modal radius to be between frame and buttons. * Update changelog. * Try moving changelog. --- packages/components/CHANGELOG.md | 4 ++++ packages/components/src/modal/style.scss | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index d9f196bfeb8150..33f0719b3297e0 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancements + +- `Modal`: Update corner radius to be between buttons and the site view frame, in a 2-4-8 system. ([#51254](https://github.com/WordPress/gutenberg/pull/51254)). + ### Bug Fix - `Popover`: Allow legitimate 0 positions to update popover position ([#51320](https://github.com/WordPress/gutenberg/pull/51320)). diff --git a/packages/components/src/modal/style.scss b/packages/components/src/modal/style.scss index acc43de8b7f902..4c210022b8bb6c 100644 --- a/packages/components/src/modal/style.scss +++ b/packages/components/src/modal/style.scss @@ -20,7 +20,7 @@ width: 100%; background: $white; box-shadow: $shadow-modal; - border-radius: $grid-unit-10 $grid-unit-10 0 0; + border-radius: $grid-unit-05 $grid-unit-05 0 0; overflow: hidden; // Have the content element fill the vertical space yet not overflow. display: flex; @@ -31,7 +31,7 @@ // Show a centered modal on bigger screens. @include break-small() { - border-radius: $grid-unit-10; + border-radius: $grid-unit-05; margin: auto; width: auto; min-width: $modal-min-width; From 104dd8fc211283a80ba145e27f9eb2347591b010 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 16 Jun 2023 12:43:46 +0200 Subject: [PATCH 055/163] Remove the hydration console log of the Interactivity API (#51571) --- packages/block-library/src/utils/interactivity/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-library/src/utils/interactivity/index.js b/packages/block-library/src/utils/interactivity/index.js index 6dbac1a45e88ca..71b6a2b790704d 100644 --- a/packages/block-library/src/utils/interactivity/index.js +++ b/packages/block-library/src/utils/interactivity/index.js @@ -12,6 +12,4 @@ registerDirectives(); document.addEventListener( 'DOMContentLoaded', async () => { await init(); - // eslint-disable-next-line no-console - console.log( 'Interactivity API started' ); } ); From f3e560e03ecec015af0e41e8e219c61b990038a8 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Fri, 16 Jun 2023 12:45:20 +0200 Subject: [PATCH 056/163] Mobile - Image block - Fix issue with set width and height images (#51463) * Mobile - Image block - Fix issue with set width and height images * Mobile - Update changelog --- packages/components/src/mobile/image/index.native.js | 2 +- packages/react-native-editor/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/mobile/image/index.native.js b/packages/components/src/mobile/image/index.native.js index 9d365763e027dd..7f69f0bed3afff 100644 --- a/packages/components/src/mobile/image/index.native.js +++ b/packages/components/src/mobile/image/index.native.js @@ -178,7 +178,7 @@ const ImageComponent = ( { imageData && containerSize && { height: - imageData?.width > containerSize?.width + imageData?.width > containerSize?.width && ! imageWidth ? containerSize?.width / imageData?.aspectRatio : undefined, }, diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 467840e6ec62c6..b6b486c7a60735 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -10,6 +10,7 @@ For each user feature we should also add a importance categorization label to i --> ## Unreleased +- [*] Image block - Fix issue where in some cases the image doesn't display the right aspect ratio [#51463] ## 1.97.0 - [**] [iOS] Fix dictation regression, in which typing/dictating at the same time caused content loss. [#49452] From a733f2798be77628bb7907973bc016bc139d9546 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 16 Jun 2023 14:47:03 +0400 Subject: [PATCH 057/163] Fix flaky 'Switch to Draft' action in preview e2e tests (#51564) --- test/e2e/specs/editor/various/preview.spec.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/e2e/specs/editor/various/preview.spec.js b/test/e2e/specs/editor/various/preview.spec.js index 3f5e6eef60d3dc..cc97d5741c4c11 100644 --- a/test/e2e/specs/editor/various/preview.spec.js +++ b/test/e2e/specs/editor/various/preview.spec.js @@ -104,6 +104,8 @@ test.describe( 'Preview', () => { page, previewUtils, } ) => { + await editor.openDocumentSettingsSidebar(); + const editorPage = page; // Type aaaaa in the title field. @@ -180,7 +182,10 @@ test.describe( 'Preview', () => { // Return to editor and switch to Draft. await editorPage.bringToFront(); - await page.click( 'role=button[name="Switch to draft"i]' ); + await page + .getByRole( 'region', { name: 'Editor settings' } ) + .getByRole( 'button', { name: 'Switch to draft' } ) + .click(); // FIXME: The confirmation dialog is not named yet. await page.click( 'role=dialog >> role=button[name="OK"i]' ); From cd9c52daf34b67a32649318c60ee8c8af2473a74 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 16 Jun 2023 13:13:13 +0200 Subject: [PATCH 058/163] [RNMobile] Ensure text input field is not editable when Bottom sheet cell is disabled (#51567) --- packages/components/src/mobile/bottom-sheet/cell.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js index 47c288b1311594..c29025e81d79b3 100644 --- a/packages/components/src/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -257,7 +257,7 @@ class BottomSheetCell extends Component { placeholder={ valuePlaceholder } placeholderTextColor={ placeholderTextColor } onChangeText={ onChangeValue } - editable={ isValueEditable } + editable={ isValueEditable && ! disabled } pointerEvents={ this.state.isEditingValue ? 'auto' : 'none' } From 89df866f4c581c3f4c8109e3e8c655263b3840e5 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 16 Jun 2023 15:48:15 +0400 Subject: [PATCH 059/163] Fix flaky Image block interactivity e2e test (#51573) --- test/e2e/specs/editor/blocks/image.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index 3905fc12162863..4709144ee9affe 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -1075,6 +1075,8 @@ test.describe( 'Image - interactivity', () => { editor, page, } ) => { + await editor.openDocumentSettingsSidebar(); + const imageBlockFromUrl = editor.canvas.locator( 'role=document[name="Block: Image"i]' ); From a1699a2c3576875fe0aa3ddccda08ebad4275ff4 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Fri, 16 Jun 2023 13:24:00 +0100 Subject: [PATCH 060/163] Browse Mode: Move CSS to more generic selector (#51547) --- .../sidebar-navigation-screen-template-part/style.scss | 3 --- .../src/components/sidebar-navigation-screen/style.scss | 7 +++++++ packages/edit-site/src/style.scss | 1 - 3 files changed, 7 insertions(+), 4 deletions(-) delete mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-template-part/style.scss diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template-part/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-template-part/style.scss deleted file mode 100644 index 399975eccb0964..00000000000000 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template-part/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.edit-site-sidebar-navigation-screen-template-part-navigation-menu__title.components-heading { - margin-bottom: $grid-unit-10; -} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index 0c864deff86d75..9b54e06ca77e72 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -14,13 +14,19 @@ .edit-site-sidebar-navigation-screen__content { color: $gray-400; padding: 0 $grid-unit-20; + .components-item-group { margin-left: -$grid-unit-20; margin-right: -$grid-unit-20; } + .components-text { color: $gray-400; } + + .components-heading { + margin-bottom: $grid-unit-10; + } } .edit-site-sidebar-navigation-screen__meta { @@ -90,3 +96,4 @@ margin: $grid-unit-20 0 0; border-top: 1px solid $gray-800; } + diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 6dde831d7273fd..916d8291532449 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -33,7 +33,6 @@ @import "./components/sidebar-navigation-screen-navigation-menu/style.scss"; @import "./components/sidebar-navigation-screen-page/style.scss"; @import "./components/sidebar-navigation-screen-template/style.scss"; -@import "./components/sidebar-navigation-screen-template-part/style.scss"; @import "./components/sidebar-navigation-subtitle/style.scss"; @import "./components/site-hub/style.scss"; @import "./components/sidebar-navigation-screen-navigation-menus/style.scss"; From 403e18601a35dd01151ae5b72293128684720792 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 16 Jun 2023 13:34:55 +0100 Subject: [PATCH 061/163] Move Navigation fallback files to 6.3 directory (#51572) --- .../class-wp-classic-to-block-menu-converter.php | 0 .../class-wp-navigation-fallback-gutenberg.php | 0 .../class-wp-rest-navigation-fallback-controller.php | 0 .../wordpress-6.3}/navigation-fallback.php | 0 lib/load.php | 5 +++-- .../class-wp-navigation-fallback-gutenberg-test.php | 0 6 files changed, 3 insertions(+), 2 deletions(-) rename lib/{experimental => compat/wordpress-6.3}/class-wp-classic-to-block-menu-converter.php (100%) rename lib/{experimental => compat/wordpress-6.3}/class-wp-navigation-fallback-gutenberg.php (100%) rename lib/{experimental => compat/wordpress-6.3}/class-wp-rest-navigation-fallback-controller.php (100%) rename lib/{experimental => compat/wordpress-6.3}/navigation-fallback.php (100%) rename phpunit/{experimental => }/class-wp-navigation-fallback-gutenberg-test.php (100%) diff --git a/lib/experimental/class-wp-classic-to-block-menu-converter.php b/lib/compat/wordpress-6.3/class-wp-classic-to-block-menu-converter.php similarity index 100% rename from lib/experimental/class-wp-classic-to-block-menu-converter.php rename to lib/compat/wordpress-6.3/class-wp-classic-to-block-menu-converter.php diff --git a/lib/experimental/class-wp-navigation-fallback-gutenberg.php b/lib/compat/wordpress-6.3/class-wp-navigation-fallback-gutenberg.php similarity index 100% rename from lib/experimental/class-wp-navigation-fallback-gutenberg.php rename to lib/compat/wordpress-6.3/class-wp-navigation-fallback-gutenberg.php diff --git a/lib/experimental/class-wp-rest-navigation-fallback-controller.php b/lib/compat/wordpress-6.3/class-wp-rest-navigation-fallback-controller.php similarity index 100% rename from lib/experimental/class-wp-rest-navigation-fallback-controller.php rename to lib/compat/wordpress-6.3/class-wp-rest-navigation-fallback-controller.php diff --git a/lib/experimental/navigation-fallback.php b/lib/compat/wordpress-6.3/navigation-fallback.php similarity index 100% rename from lib/experimental/navigation-fallback.php rename to lib/compat/wordpress-6.3/navigation-fallback.php diff --git a/lib/load.php b/lib/load.php index 1fd251bd19e9b0..ceecaf7f894783 100644 --- a/lib/load.php +++ b/lib/load.php @@ -47,6 +47,7 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-global-styles-revisions-controller-6-3.php'; + require_once __DIR__ . '/compat/wordpress-6.3/class-wp-rest-navigation-fallback-controller.php'; require_once __DIR__ . '/compat/wordpress-6.3/rest-api.php'; require_once __DIR__ . '/compat/wordpress-6.3/theme-previews.php'; require_once __DIR__ . '/compat/wordpress-6.3/navigation-block-preloading.php'; @@ -58,7 +59,6 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/experimental/class-wp-rest-customizer-nonces.php'; } - require_once __DIR__ . '/experimental/class-wp-rest-navigation-fallback-controller.php'; require_once __DIR__ . '/experimental/rest-api.php'; } @@ -93,6 +93,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php'; require __DIR__ . '/compat/wordpress-6.3/script-loader.php'; require __DIR__ . '/compat/wordpress-6.3/blocks.php'; +require __DIR__ . '/compat/wordpress-6.3/navigation-fallback.php'; // Experimental features. remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); // Turns off WP 6.0's stopgap handler for Webfonts API. @@ -102,7 +103,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/experimental/navigation-theme-opt-in.php'; require __DIR__ . '/experimental/kses.php'; require __DIR__ . '/experimental/l10n.php'; -require __DIR__ . '/experimental/navigation-fallback.php'; + require __DIR__ . '/experimental/interactivity-api/script-loader.php'; if ( gutenberg_is_experiment_enabled( 'gutenberg-interactivity-api-core-blocks' ) ) { require __DIR__ . '/experimental/interactivity-api/blocks.php'; diff --git a/phpunit/experimental/class-wp-navigation-fallback-gutenberg-test.php b/phpunit/class-wp-navigation-fallback-gutenberg-test.php similarity index 100% rename from phpunit/experimental/class-wp-navigation-fallback-gutenberg-test.php rename to phpunit/class-wp-navigation-fallback-gutenberg-test.php From a72b4b11befc30d680c29e8f314439b59df39afd Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Fri, 16 Jun 2023 08:40:02 -0400 Subject: [PATCH 062/163] Tweak more icons for HiDPI devices (#51511) * Update post-content.js * Update title.js * Update page-break.js * Update link-off.js * Update link.js * Run linkOff through SVGOMG --- packages/icons/src/library/link-off.js | 2 +- packages/icons/src/library/link.js | 2 +- packages/icons/src/library/page-break.js | 2 +- packages/icons/src/library/post-content.js | 2 +- packages/icons/src/library/title.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/icons/src/library/link-off.js b/packages/icons/src/library/link-off.js index 0ba44b747753a3..ff03679aa3f5dd 100644 --- a/packages/icons/src/library/link-off.js +++ b/packages/icons/src/library/link-off.js @@ -5,7 +5,7 @@ import { SVG, Path } from '@wordpress/primitives'; const linkOff = ( - + ); diff --git a/packages/icons/src/library/link.js b/packages/icons/src/library/link.js index 48375903deb2c6..81ff6b82e48fc2 100644 --- a/packages/icons/src/library/link.js +++ b/packages/icons/src/library/link.js @@ -5,7 +5,7 @@ import { SVG, Path } from '@wordpress/primitives'; const link = ( - + ); diff --git a/packages/icons/src/library/page-break.js b/packages/icons/src/library/page-break.js index ef8a0a82869d15..a382b5d41bf452 100644 --- a/packages/icons/src/library/page-break.js +++ b/packages/icons/src/library/page-break.js @@ -5,7 +5,7 @@ import { Path, SVG } from '@wordpress/primitives'; const pageBreak = ( - + ); diff --git a/packages/icons/src/library/post-content.js b/packages/icons/src/library/post-content.js index fac55cf5f4e194..4f9d6ce56ed83f 100644 --- a/packages/icons/src/library/post-content.js +++ b/packages/icons/src/library/post-content.js @@ -5,7 +5,7 @@ import { SVG, Path } from '@wordpress/primitives'; const postContent = ( - + ); diff --git a/packages/icons/src/library/title.js b/packages/icons/src/library/title.js index 902192be392ec4..d70f2fd52f6199 100644 --- a/packages/icons/src/library/title.js +++ b/packages/icons/src/library/title.js @@ -5,7 +5,7 @@ import { SVG, Path } from '@wordpress/primitives'; const title = ( - + ); From abc528df001efa9cc96f06a23ee8e3f24bb57e24 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Fri, 16 Jun 2023 15:33:52 +0200 Subject: [PATCH 063/163] Babel config: enable useSpread option for JSX transform to reduce transpilation (#51574) --- packages/babel-preset-default/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/babel-preset-default/index.js b/packages/babel-preset-default/index.js index 99d2cdf0cb4a37..cb5ed0ccac3b12 100644 --- a/packages/babel-preset-default/index.js +++ b/packages/babel-preset-default/index.js @@ -89,6 +89,7 @@ module.exports = ( api ) => { { pragma: 'createElement', pragmaFrag: 'Fragment', + useSpread: true, }, ], maybeGetPluginTransformRuntime(), From 474b550ecb6a8b7a5c15a1c2adc7bc946f6d25fe Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Fri, 16 Jun 2023 17:11:42 +0300 Subject: [PATCH 064/163] Fix fixed toolbar in customize widgets (#51092) * override fixed block toolbar styles in the customize widgets editor * show the collapse button and style collapsed block toolbar * lower the z-index of the block toolbar in widgets customizer --- packages/customize-widgets/src/style.scss | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/customize-widgets/src/style.scss b/packages/customize-widgets/src/style.scss index 3bf341c34c0eb1..bd6d16b89c7fa7 100644 --- a/packages/customize-widgets/src/style.scss +++ b/packages/customize-widgets/src/style.scss @@ -17,3 +17,34 @@ .customize-widgets-popover { @include reset; } + +/** + Fixed bloock toolbar overrides. We can't detect each editor instance + in the styles of the block editor component so we need to override + the fixed styles here because the breakpoint css does not fire in the + customizer's left panel. +*/ +.block-editor-block-contextual-toolbar { + &.is-fixed { + position: sticky; + top: 0; + left: 0; + z-index: z-index(".block-editor-block-list__insertion-point"); + width: calc(100% + 2 * 12px); //12px is the padding of customizer sidebar content + + overflow-y: hidden; + + border: none; + border-bottom: $border-width solid $gray-200; + border-radius: 0; + + .block-editor-block-toolbar .components-toolbar-group, + .block-editor-block-toolbar .components-toolbar { + border-right-color: $gray-200; + } + + &.is-collapsed { + margin-left: -12px; //12px is the padding of customizer sidebar content + } + } +} From a96091b7c4e431df2bb4c6f7959518bd6b1c3954 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Fri, 16 Jun 2023 16:25:11 +0100 Subject: [PATCH 065/163] Fix navigation error in library (#51589) * Navigation: Browse Mode: Fix misaligned ellipsis * use a more accurate value * update comment * revert unrelated change * Navigation in Browse Mode: Fix error in Library * revert CSS change --- .../template-part-navigation-menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template-part/template-part-navigation-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-template-part/template-part-navigation-menu.js index 4837ad99beaddb..f451c17e00adb7 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template-part/template-part-navigation-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template-part/template-part-navigation-menu.js @@ -22,7 +22,7 @@ export default function TemplatePartNavigationMenu( { id } ) { size="12" upperCase={ true } > - { title || __( 'Navigation' ) } + { title?.rendered || __( 'Navigation' ) } From 1d9c4fa34b9ca11dc41a7fd35116b9bcf1e0bfc8 Mon Sep 17 00:00:00 2001 From: Pooja Killekar <41000648+pooja-muchandikar@users.noreply.github.com> Date: Fri, 16 Jun 2023 21:26:37 +0530 Subject: [PATCH 066/163] Migrate Navigable toolbar test to Playwright (#51514) Removed tests related to the alt + 10 shortcut as they are covered elsewhere. --- .../editor/various/navigable-toolbar.test.js | 104 ------------------ .../editor/various/navigable-toolbar.spec.js | 48 ++++++++ 2 files changed, 48 insertions(+), 104 deletions(-) delete mode 100644 packages/e2e-tests/specs/editor/various/navigable-toolbar.test.js create mode 100644 test/e2e/specs/editor/various/navigable-toolbar.spec.js diff --git a/packages/e2e-tests/specs/editor/various/navigable-toolbar.test.js b/packages/e2e-tests/specs/editor/various/navigable-toolbar.test.js deleted file mode 100644 index c2735f8fbf75c9..00000000000000 --- a/packages/e2e-tests/specs/editor/various/navigable-toolbar.test.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * WordPress dependencies - */ -import { createNewPost, pressKeyWithModifier } from '@wordpress/e2e-test-utils'; - -async function isInBlockToolbar() { - return await page.evaluate( () => { - return !! document.activeElement.closest( - '.block-editor-block-toolbar' - ); - } ); -} - -describe( 'Block Toolbar', () => { - beforeEach( async () => { - await createNewPost(); - } ); - - describe( 'Contextual Toolbar', () => { - it( 'should not scroll page', async () => { - while ( - await page.evaluate( () => { - const { activeElement } = - document.activeElement?.contentDocument ?? document; - const scrollable = - wp.dom.getScrollContainer( activeElement ); - return ! scrollable || scrollable.scrollTop === 0; - } ) - ) { - await page.keyboard.press( 'Enter' ); - } - - await page.keyboard.type( 'a' ); - - const scrollTopBefore = await page.evaluate( () => { - const { activeElement } = - document.activeElement?.contentDocument ?? document; - window.scrollContainer = - wp.dom.getScrollContainer( activeElement ); - return window.scrollContainer.scrollTop; - } ); - - await pressKeyWithModifier( 'alt', 'F10' ); - expect( await isInBlockToolbar() ).toBe( true ); - - const scrollTopAfter = await page.evaluate( () => { - return window.scrollContainer.scrollTop; - } ); - expect( scrollTopBefore ).toBe( scrollTopAfter ); - } ); - - it( 'navigates into the toolbar by keyboard (Alt+F10)', async () => { - // Assumes new post focus starts in title. Create first new - // block by Enter. - await page.keyboard.press( 'Enter' ); - - // [TEMPORARY]: A new paragraph is not technically a block yet - // until starting to type within it. - await page.keyboard.type( 'Example' ); - - // Upward. - await pressKeyWithModifier( 'alt', 'F10' ); - - expect( await isInBlockToolbar() ).toBe( true ); - } ); - } ); - - describe( 'Unified Toolbar', () => { - beforeEach( async () => { - // Enable unified toolbar - await page.evaluate( () => { - const { select, dispatch } = wp.data; - const isCurrentlyUnified = - select( 'core/edit-post' ).isFeatureActive( - 'fixedToolbar' - ); - if ( ! isCurrentlyUnified ) { - dispatch( 'core/edit-post' ).toggleFeature( - 'fixedToolbar' - ); - } - } ); - } ); - - it( 'navigates into the toolbar by keyboard (Alt+F10)', async () => { - // Assumes new post focus starts in title. Create first new - // block by Enter. - await page.keyboard.press( 'Enter' ); - - // [TEMPORARY]: A new paragraph is not technically a block yet - // until starting to type within it. - await page.keyboard.type( 'Example' ); - - // Upward. - await pressKeyWithModifier( 'alt', 'F10' ); - - expect( - await page.evaluate( () => { - return document.activeElement.getAttribute( 'aria-label' ); - } ) - ).toBe( 'Show document tools' ); - } ); - } ); -} ); diff --git a/test/e2e/specs/editor/various/navigable-toolbar.spec.js b/test/e2e/specs/editor/various/navigable-toolbar.spec.js new file mode 100644 index 00000000000000..d6524eeabb64b2 --- /dev/null +++ b/test/e2e/specs/editor/various/navigable-toolbar.spec.js @@ -0,0 +1,48 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Block Toolbar', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test.describe( 'Contextual Toolbar', () => { + test( 'should not scroll page', async ( { page, pageUtils } ) => { + while ( + await page.evaluate( () => { + const { activeElement } = + document.activeElement?.contentDocument ?? document; + const scrollable = + window.wp.dom.getScrollContainer( activeElement ); + return ! scrollable || scrollable.scrollTop === 0; + } ) + ) { + await page.keyboard.press( 'Enter' ); + } + + await page.keyboard.type( 'a' ); + + const scrollTopBefore = await page.evaluate( () => { + const { activeElement } = + document.activeElement?.contentDocument ?? document; + window.scrollContainer = + window.wp.dom.getScrollContainer( activeElement ); + return window.scrollContainer.scrollTop; + } ); + + await pageUtils.pressKeys( 'alt+F10' ); + await expect( + page + .getByRole( 'toolbar', { name: 'Block Tools' } ) + .getByRole( 'button', { name: 'Paragraph' } ) + ).toBeFocused(); + + const scrollTopAfter = await page.evaluate( () => { + return window.scrollContainer.scrollTop; + } ); + expect( scrollTopBefore ).toBe( scrollTopAfter ); + } ); + } ); +} ); From 7b07b67f2dc49d4ec6883e1470276e294e451f05 Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Fri, 16 Jun 2023 12:56:50 -0500 Subject: [PATCH 067/163] Set fixedToolbar to false after each top toolbar test to ensure proper cleanup (#51600) --- .../editor/various/shortcut-focus-toolbar.spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js b/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js index 9a97d1a00310ac..05105123546289 100644 --- a/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js +++ b/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js @@ -82,6 +82,11 @@ test.describe( 'Focus toolbar shortcut (alt + F10)', () => { await editor.setIsFixedToolbar( true ); } ); + test.afterEach( async ( { editor } ) => { + // Ensure the fixed toolbar option is off + await editor.setIsFixedToolbar( false ); + } ); + test( 'Focuses the correct toolbar in edit mode', async ( { editor, page, @@ -161,11 +166,6 @@ test.describe( 'Focus toolbar shortcut (alt + F10)', () => { }, } ); - test.beforeEach( async ( { editor } ) => { - // Ensure the fixed toolbar option is off - await editor.setIsFixedToolbar( false ); - } ); - test( 'Focuses the correct toolbar in edit mode', async ( { editor, page, From e3194a1d1d9869560260a3bba0939a28a52e737f Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 16 Jun 2023 21:00:10 +0200 Subject: [PATCH 068/163] Mobile Release v1.97.1 (#51577) * Release script: Update react-native-editor version to 1.97.0 * Release script: Update with changes from 'npm run core preios' * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md Fix white space * Release script: Update react-native-editor version to 1.97.1 * Release script: Update with changes from 'npm run core preios' * [RNMobile] Fix crash when using the delete key to remove a single button (#51435) * Fix crash when using the delete key to remove a single button * Add onDeleteBlock prop to block.native.js * Update button edit.native.js snapshots * Update button delete test to focus button input * Update button test to use triggerBlockListLayout * [RNMobile] Ensure text input field is not editable when Bottom sheet cell is disabled (#51567) * Update `react-native-editor` changelog --------- Co-authored-by: jhnstn Co-authored-by: Jason Johnston Co-authored-by: Derek Blank --- packages/react-native-aztec/package.json | 2 +- packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/CHANGELOG.md | 4 ++++ packages/react-native-editor/ios/Podfile.lock | 8 ++++---- packages/react-native-editor/package.json | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index e85161e8f812c1..61bc49cf18880b 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.97.0", + "version": "1.97.1", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index ee6a5af3f5b424..157b108480c6fd 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.97.0", + "version": "1.97.1", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index b6b486c7a60735..7ce556139a3ec4 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -12,6 +12,10 @@ For each user feature we should also add a importance categorization label to i ## Unreleased - [*] Image block - Fix issue where in some cases the image doesn't display the right aspect ratio [#51463] +## 1.97.1 +- [**] Fix crash when using the delete key to remove a single button [#51435] +- [*] Ensure text input field is not editable when Bottom sheet cell is disabled [#51567] + ## 1.97.0 - [**] [iOS] Fix dictation regression, in which typing/dictating at the same time caused content loss. [#49452] - [*] [internal] Upgrade compile and target sdk version to Android API 33 [#50731] diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 8ae280a2b08f48..8db86747eeafa7 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.69.4) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.97.0): + - Gutenberg (1.97.1): - React-Core (= 0.69.4) - React-CoreModules (= 0.69.4) - React-RCTImage (= 0.69.4) @@ -360,7 +360,7 @@ PODS: - React-Core - RNSVG (9.13.6): - React-Core - - RNTAztecView (1.97.0): + - RNTAztecView (1.97.1): - React-Core - WordPress-Aztec-iOS (~> 1.19.8) - SDWebImage (5.11.1): @@ -540,7 +540,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 2ff441cbe6e58c1778d8a5cf3311831a6a8c0809 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a - Gutenberg: 75e04d6a42ecfb201115f1ccbb066680915a35aa + Gutenberg: 55f585d8343f7b498bf868e858ccd783c6956c13 libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a RCTRequired: bd9d2ab0fda10171fcbcf9ba61a7df4dc15a28f4 @@ -582,7 +582,7 @@ SPEC CHECKSUMS: RNReanimated: 5740ec9926f80bccd404bacd3e71108e87c94afa RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 - RNTAztecView: 221ecdc2998b979bd0994b9100101ba755cbb1d8 + RNTAztecView: 971e2603ff0b8a477b16e49fc0fd335cb6137887 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index cfea612a2dcf80..5692ce20ebcb24 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.97.0", + "version": "1.97.1", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 53d02265578c3373e3d8fd0051ea2ae2c102aa21 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Sat, 17 Jun 2023 08:44:59 +0400 Subject: [PATCH 069/163] Fix flaky 'Keep styles on block transforms' e2e test (#51593) --- .../editor/various/keep-styles-on-block-transforms.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js b/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js index 35895f05209be1..c8ca539bda4150 100644 --- a/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js +++ b/test/e2e/specs/editor/various/keep-styles-on-block-transforms.spec.js @@ -12,9 +12,9 @@ test.describe( 'Keep styles on block transforms', () => { page, editor, } ) => { + await editor.openDocumentSettingsSidebar(); await editor.canvas.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( '## Heading' ); - await editor.openDocumentSettingsSidebar(); await page.click( 'role=button[name="Color Text styles"i]' ); await page.click( 'role=button[name="Color: Luminous vivid orange"i]' ); @@ -44,7 +44,7 @@ test.describe( 'Keep styles on block transforms', () => { title: 'test', } ); } ); - // Create a paragraph block with some content. + await editor.openDocumentSettingsSidebar(); await editor.canvas.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'Line 1 to be made large' ); await page.keyboard.press( 'Enter' ); @@ -77,7 +77,7 @@ test.describe( 'Keep styles on block transforms', () => { page, editor, } ) => { - // Create a paragraph block with some content. + await editor.openDocumentSettingsSidebar(); await editor.canvas.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'Line 1 to be made large' ); await page.click( 'role=radio[name="Large"i]' ); From 54d63c89f1911ab29cd83791f41a054910b3c037 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Sat, 17 Jun 2023 09:22:12 +0400 Subject: [PATCH 070/163] Fix the flaky site editor list view tests (#51598) --- .../specs/site-editor/block-list-panel-preference.spec.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/e2e/specs/site-editor/block-list-panel-preference.spec.js b/test/e2e/specs/site-editor/block-list-panel-preference.spec.js index d933714017977e..7a6e83d09cf7ca 100644 --- a/test/e2e/specs/site-editor/block-list-panel-preference.spec.js +++ b/test/e2e/specs/site-editor/block-list-panel-preference.spec.js @@ -38,5 +38,12 @@ test.describe( 'Block list view', () => { await expect( page.locator( 'role=region[name="List View"i]' ) ).toBeVisible(); + + // The preferences cleanup. + await page.evaluate( () => { + window.wp.data + .dispatch( 'core/preferences' ) + .set( 'core/edit-site', 'showListViewByDefault', false ); + } ); } ); } ); From bc70b39ff920ce6416f52f10455169bf1e003307 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Sat, 17 Jun 2023 09:35:13 +0400 Subject: [PATCH 071/163] Fix flaky 'hooks API' e2e test (#51592) --- test/e2e/specs/editor/plugins/hooks-api.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/e2e/specs/editor/plugins/hooks-api.spec.js b/test/e2e/specs/editor/plugins/hooks-api.spec.js index 7257ccde729642..9af8e6570eef32 100644 --- a/test/e2e/specs/editor/plugins/hooks-api.spec.js +++ b/test/e2e/specs/editor/plugins/hooks-api.spec.js @@ -21,6 +21,7 @@ test.describe( 'Using Hooks API', () => { page, editor, } ) => { + await editor.openDocumentSettingsSidebar(); await editor.canvas.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'First paragraph' ); await page.click( @@ -35,6 +36,7 @@ test.describe( 'Using Hooks API', () => { editor, page, } ) => { + await editor.openDocumentSettingsSidebar(); await editor.canvas.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'First paragraph' ); From 3749de52114436246a8f149c9d3959608820e488 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Sat, 17 Jun 2023 10:39:14 +0200 Subject: [PATCH 072/163] Add init modules to details and post-time-to-read blocks (#51606) --- packages/block-library/src/details/init.js | 6 ++++++ packages/block-library/src/post-time-to-read/init.js | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 packages/block-library/src/details/init.js create mode 100644 packages/block-library/src/post-time-to-read/init.js diff --git a/packages/block-library/src/details/init.js b/packages/block-library/src/details/init.js new file mode 100644 index 00000000000000..79f0492c2cb2f8 --- /dev/null +++ b/packages/block-library/src/details/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/post-time-to-read/init.js b/packages/block-library/src/post-time-to-read/init.js new file mode 100644 index 00000000000000..79f0492c2cb2f8 --- /dev/null +++ b/packages/block-library/src/post-time-to-read/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); From c8d7e6943fde0836515f6936f46ad2dcf07690f1 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Sat, 17 Jun 2023 19:11:36 +0900 Subject: [PATCH 073/163] Button: Remove unnecessary margin from dashicon (#51395) * Add vizreg test * Button: Remove unnecessary margin from dashicon * Update changelog * Revert some changes * Layout with padding instead of margin --------- Co-authored-by: Lena Morita --- packages/components/CHANGELOG.md | 1 + .../src/button/stories/e2e/index.tsx | 22 +++++++++++++++++++ packages/components/src/button/style.scss | 18 +++++---------- .../storybook-playwright/specs/button.spec.ts | 16 ++++++++++++++ test/storybook-playwright/utils.ts | 4 ++-- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 33f0719b3297e0..629513035d62ad 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug Fix - `Popover`: Allow legitimate 0 positions to update popover position ([#51320](https://github.com/WordPress/gutenberg/pull/51320)). +- `Button`: Remove unnecessary margin from dashicon ([#51395](https://github.com/WordPress/gutenberg/pull/51395)). ### Internal diff --git a/packages/components/src/button/stories/e2e/index.tsx b/packages/components/src/button/stories/e2e/index.tsx index e68cb67a524daf..0d955b5b6c5a85 100644 --- a/packages/components/src/button/stories/e2e/index.tsx +++ b/packages/components/src/button/stories/e2e/index.tsx @@ -56,3 +56,25 @@ export const Icon = VariantStates.bind( {} ); Icon.args = { icon: wordpress, }; + +export const Dashicons: ComponentStory< typeof Button > = ( props ) => { + return ( +
    + + +
    + ); +}; +Dashicons.args = { + icon: 'editor-help', + variant: 'primary', +}; diff --git a/packages/components/src/button/style.scss b/packages/components/src/button/style.scss index 5916876aad589d..c93d7e2070c43b 100644 --- a/packages/components/src/button/style.scss +++ b/packages/components/src/button/style.scss @@ -168,11 +168,6 @@ background: rgba(var(--wp-admin-theme-color--rgb), 0.08); } - .dashicon { - display: inline-block; - flex: 0 0 auto; - } - // Pull left if the tertiary button stands alone after a description, so as to vertically align with items above. p + & { margin-left: -($grid-unit-15 * 0.5); @@ -296,10 +291,11 @@ } .dashicon { - display: inline-block; - flex: 0 0 auto; - margin-left: 2px; - margin-right: 2px; + display: inline-flex; + justify-content: center; + align-items: center; + padding: 2px; + box-sizing: content-box; } &.has-text { @@ -308,10 +304,6 @@ padding-left: $grid-unit-10; gap: $grid-unit-05; } - - &.has-text .dashicon { - margin-right: $grid-unit-10 + 2px; - } } // Toggled style. diff --git a/test/storybook-playwright/specs/button.spec.ts b/test/storybook-playwright/specs/button.spec.ts index 66049d8c63f4cc..d45fddfec0b5a1 100644 --- a/test/storybook-playwright/specs/button.spec.ts +++ b/test/storybook-playwright/specs/button.spec.ts @@ -54,4 +54,20 @@ test.describe( 'Button', () => { } ); } ); } ); + + test.describe( 'dashicon', () => { + test.beforeEach( async ( { page } ) => { + await gotoStoryId( page, 'components-button--dashicons', { + decorators: { css: 'wordpress' }, + } ); + // Wait for dashicons font to load + await page.waitForFunction( () => + document.fonts.check( '20px dashicons' ) + ); + } ); + + test( 'should render with correct spacing', async ( { page } ) => { + expect( await page.screenshot() ).toMatchSnapshot(); + } ); + } ); } ); diff --git a/test/storybook-playwright/utils.ts b/test/storybook-playwright/utils.ts index 1f0fe7235abeed..61ab7b3552edd2 100644 --- a/test/storybook-playwright/utils.ts +++ b/test/storybook-playwright/utils.ts @@ -21,7 +21,7 @@ const buildDecoratorString = ( decorators: Decorators = {} ) => { return decoratorParamStrings.join( ';' ); }; -export const gotoStoryId = ( +export const gotoStoryId = async ( page: Page, storyId: string, { decorators }: Options = {} @@ -35,7 +35,7 @@ export const gotoStoryId = ( params.set( 'id', storyId ); - page.goto( + await page.goto( `http://localhost:${ STORYBOOK_PORT }/iframe.html?${ params.toString() }`, { waitUntil: 'load' } ); From bc7ac22b9d25a3769d91e6cd86f17ffcb0982809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Walb=C3=B8=20Johnsg=C3=A5rd?= Date: Sat, 17 Jun 2023 12:16:32 +0200 Subject: [PATCH 074/163] Site editor navigation: Use chevron left in RTL mode (#51588) --- .../src/components/sidebar-navigation-item/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-item/index.js b/packages/edit-site/src/components/sidebar-navigation-item/index.js index 244efdf0f869c1..18b8ea42516cbe 100644 --- a/packages/edit-site/src/components/sidebar-navigation-item/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-item/index.js @@ -11,7 +11,8 @@ import { __experimentalHStack as HStack, FlexBlock, } from '@wordpress/components'; -import { chevronRightSmall, Icon } from '@wordpress/icons'; +import { isRTL } from '@wordpress/i18n'; +import { chevronRightSmall, chevronLeftSmall, Icon } from '@wordpress/icons'; export default function SidebarNavigationItem( { className, @@ -39,7 +40,7 @@ export default function SidebarNavigationItem( { { children } { withChevron && ( From 10d6ff022ac29dba66262915d7011009d3f21c26 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Sat, 17 Jun 2023 11:12:26 -0700 Subject: [PATCH 075/163] Update wp-env changelog (#51614) --- packages/env/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index ec9e38ed41c839..5ccf2061620ae5 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug fix + +- Address issue where a missing file in the underlying Docker image caused `wp-env` to crash. [#51513](https://github.com/WordPress/gutenberg/pull/51513) + ## 8.1.0 (2023-06-07) ### New feature From 87718cd314847c8e4393cf775bf557674a726e51 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Sat, 17 Jun 2023 18:42:31 +0000 Subject: [PATCH 076/163] Update changelog files --- packages/env/CHANGELOG.md | 2 ++ packages/env/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 5ccf2061620ae5..03114e7f631d8e 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.1.1 (2023-06-17) + ### Bug fix - Address issue where a missing file in the underlying Docker image caused `wp-env` to crash. [#51513](https://github.com/WordPress/gutenberg/pull/51513) diff --git a/packages/env/package.json b/packages/env/package.json index a90359b537c62c..93b7bd4bf49627 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "8.1.0", + "version": "8.1.1-prerelease", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 833fae17c4eb570e03de26cdc8d6038a615482c5 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Sat, 17 Jun 2023 18:44:58 +0000 Subject: [PATCH 077/163] chore(release): publish - @wordpress/env@8.1.1 --- packages/env/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/env/package.json b/packages/env/package.json index 93b7bd4bf49627..2d655ec01b13d0 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "8.1.1-prerelease", + "version": "8.1.1", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From a67d4789165e52660f30f9f8998ddb0d399deb64 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:01:05 +1000 Subject: [PATCH 078/163] List View: add an indicator of when a position type is set for a block (#49122) * List View: add a position label when a position type is set for a block * Move get label function to the useBlockDisplayInformation file * Try using icon over the block icon instead of a text label to indicate sticky status * Try more subtle icon / styling idea, and add in a tooltip * Try provided svg icons * Don't change color when hovered * Try separate sticky icon instead * Add pinSmall icon * Minify pinSmall * Add additional optional chaining because attributes can technically be null --------- Co-authored-by: James Koster --- .../list-view/block-select-button.js | 20 +++++++++++++- .../src/components/list-view/style.scss | 3 ++- .../use-block-display-information/index.js | 26 +++++++++++++++++++ packages/icons/src/index.js | 1 + packages/icons/src/library/pin-small.js | 18 +++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 packages/icons/src/library/pin-small.js diff --git a/packages/block-editor/src/components/list-view/block-select-button.js b/packages/block-editor/src/components/list-view/block-select-button.js index ca5e414ae65769..b6517357e7b657 100644 --- a/packages/block-editor/src/components/list-view/block-select-button.js +++ b/packages/block-editor/src/components/list-view/block-select-button.js @@ -10,12 +10,14 @@ import { Button, __experimentalHStack as HStack, __experimentalTruncate as Truncate, + Tooltip, } from '@wordpress/components'; import { forwardRef } from '@wordpress/element'; -import { Icon, lockSmall as lock } from '@wordpress/icons'; +import { Icon, lockSmall as lock, pinSmall } from '@wordpress/icons'; import { SPACE, ENTER, BACKSPACE, DELETE } from '@wordpress/keycodes'; import { useSelect, useDispatch } from '@wordpress/data'; import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts'; +import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies @@ -60,6 +62,15 @@ function ListViewBlockSelectButton( } = useSelect( blockEditorStore ); const { removeBlocks } = useDispatch( blockEditorStore ); const isMatch = useShortcutEventMatch(); + const isSticky = blockInformation?.positionType === 'sticky'; + + const positionLabel = blockInformation?.positionLabel + ? sprintf( + // translators: 1: Position of selected block, e.g. "Sticky" or "Fixed". + __( 'Position: %1$s' ), + blockInformation.positionLabel + ) + : ''; // The `href` attribute triggers the browser's native HTML drag operations. // When the link is dragged, the element's outerHTML is set in DataTransfer object as text/html. @@ -166,6 +177,13 @@ function ListViewBlockSelectButton( ) } + { positionLabel && isSticky && ( + + + + + + ) } { isLocked && ( diff --git a/packages/block-editor/src/components/list-view/style.scss b/packages/block-editor/src/components/list-view/style.scss index 082389f71d4a0e..4b3ba296d35bc8 100644 --- a/packages/block-editor/src/components/list-view/style.scss +++ b/packages/block-editor/src/components/list-view/style.scss @@ -335,7 +335,8 @@ background: rgba($black, 0.3); } - .block-editor-list-view-block-select-button__lock { + .block-editor-list-view-block-select-button__lock, + .block-editor-list-view-block-select-button__sticky { line-height: 0; } } diff --git a/packages/block-editor/src/components/use-block-display-information/index.js b/packages/block-editor/src/components/use-block-display-information/index.js index 950cb9c905b24c..87909cea45f637 100644 --- a/packages/block-editor/src/components/use-block-display-information/index.js +++ b/packages/block-editor/src/components/use-block-display-information/index.js @@ -7,6 +7,7 @@ import { isReusableBlock, isTemplatePart, } from '@wordpress/blocks'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -27,6 +28,26 @@ import { store as blockEditorStore } from '../../store'; * @property {string} anchor HTML anchor. */ +/** + * Get the display label for a block's position type. + * + * @param {Object} attributes Block attributes. + * @return {string} The position type label. + */ +function getPositionTypeLabel( attributes ) { + const positionType = attributes?.style?.position?.type; + + if ( positionType === 'sticky' ) { + return __( 'Sticky' ); + } + + if ( positionType === 'fixed' ) { + return __( 'Fixed' ); + } + + return null; +} + /** * Hook used to try to find a matching block variation and return * the appropriate information for display reasons. In order to @@ -57,12 +78,15 @@ export default function useBlockDisplayInformation( clientId ) { const match = getActiveBlockVariation( blockName, attributes ); const isSynced = isReusableBlock( blockType ) || isTemplatePart( blockType ); + const positionLabel = getPositionTypeLabel( attributes ); const blockTypeInfo = { isSynced, title: blockType.title, icon: blockType.icon, description: blockType.description, anchor: attributes?.anchor, + positionLabel, + positionType: attributes?.style?.position?.type, }; if ( ! match ) return blockTypeInfo; @@ -72,6 +96,8 @@ export default function useBlockDisplayInformation( clientId ) { icon: match.icon || blockType.icon, description: match.description || blockType.description, anchor: attributes?.anchor, + positionLabel, + positionType: attributes?.style?.position?.type, }; }, [ clientId ] diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js index cd8058425a8d68..903d6f9455a873 100644 --- a/packages/icons/src/index.js +++ b/packages/icons/src/index.js @@ -169,6 +169,7 @@ export { default as positionRight } from './library/position-right'; export { default as pencil } from './library/pencil'; export { default as people } from './library/people'; export { default as pin } from './library/pin'; +export { default as pinSmall } from './library/pin-small'; export { default as plugins } from './library/plugins'; export { default as plusCircleFilled } from './library/plus-circle-filled'; export { default as plusCircle } from './library/plus-circle'; diff --git a/packages/icons/src/library/pin-small.js b/packages/icons/src/library/pin-small.js new file mode 100644 index 00000000000000..7ed84e47949a23 --- /dev/null +++ b/packages/icons/src/library/pin-small.js @@ -0,0 +1,18 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const pinSmall = ( + + + +); + +export default pinSmall; From 7f2ea04bb799e134fcef3e41763aaedb750ad351 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 19 Jun 2023 13:53:27 +1000 Subject: [PATCH 079/163] Reduce number of List View re-renders while typing (#51518) * Add getClientIdsTreeWithBlockEditingMode() * Rename getClientIdsTreeWithBlockEditingMode -> getListViewClientIdsTree --- .../list-view/use-list-view-client-ids.js | 23 +-- .../src/store/private-selectors.js | 34 ++++ packages/block-editor/src/store/selectors.js | 35 +++- .../src/store/test/private-selectors.js | 174 +++++++++++++++++- .../block-editor/src/store/test/selectors.js | 1 + 5 files changed, 239 insertions(+), 28 deletions(-) diff --git a/packages/block-editor/src/components/list-view/use-list-view-client-ids.js b/packages/block-editor/src/components/list-view/use-list-view-client-ids.js index 8b989591802fa7..d51412fdf2c3db 100644 --- a/packages/block-editor/src/components/list-view/use-list-view-client-ids.js +++ b/packages/block-editor/src/components/list-view/use-list-view-client-ids.js @@ -16,31 +16,14 @@ export default function useListViewClientIds( { blocks, rootClientId } ) { const { getDraggedBlockClientIds, getSelectedBlockClientIds, - __unstableGetClientIdsTree, - getBlockEditingMode, + getListViewClientIdsTree, } = unlock( select( blockEditorStore ) ); - const removeDisabledBlocks = ( tree ) => { - return tree.flatMap( ( { clientId, innerBlocks, ...rest } ) => { - if ( getBlockEditingMode( clientId ) === 'disabled' ) { - return removeDisabledBlocks( innerBlocks ); - } - return [ - { - clientId, - innerBlocks: removeDisabledBlocks( innerBlocks ), - ...rest, - }, - ]; - } ); - }; - return { selectedClientIds: getSelectedBlockClientIds(), draggedClientIds: getDraggedBlockClientIds(), - clientIdsTree: removeDisabledBlocks( - blocks ?? __unstableGetClientIdsTree( rootClientId ) - ), + clientIdsTree: + blocks ?? getListViewClientIdsTree( rootClientId ), }; }, [ blocks, rootClientId ] diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index ff85b4fb3f20b4..c250b649569f0c 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -135,3 +135,37 @@ export const isBlockSubtreeDisabled = createSelector( }, ( state ) => [ state.blockEditingModes, state.blocks.parents ] ); + +/** + * Returns a tree of block objects with only clientID and innerBlocks set. + * Blocks with a 'disabled' editing mode are not included. + * + * @param {Object} state Global application state. + * @param {?string} rootClientId Optional root client ID of block list. + * + * @return {Object[]} Tree of block objects with only clientID and innerBlocks set. + */ +export const getListViewClientIdsTree = createSelector( + ( state, rootClientId = '' ) => { + return getBlockOrder( state, rootClientId ).flatMap( ( clientId ) => { + if ( getBlockEditingMode( state, clientId ) !== 'disabled' ) { + return [ + { + clientId, + innerBlocks: getListViewClientIdsTree( + state, + clientId + ), + }, + ]; + } + return getListViewClientIdsTree( state, clientId ); + } ); + }, + ( state ) => [ + state.blocks.order, + state.blockEditingModes, + state.settings.templateLock, + state.blockListSettings, + ] +); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 128b167f697617..1616a2fde8b1b5 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -187,16 +187,27 @@ export function getBlocks( state, rootClientId ) { * Returns a stripped down block object containing only its client ID, * and its inner blocks' client IDs. * + * @deprecated + * * @param {Object} state Editor state. * @param {string} clientId Client ID of the block to get. * * @return {Object} Client IDs of the post blocks. */ export const __unstableGetClientIdWithClientIdsTree = createSelector( - ( state, clientId ) => ( { - clientId, - innerBlocks: __unstableGetClientIdsTree( state, clientId ), - } ), + ( state, clientId ) => { + deprecated( + "wp.data.select( 'core/block-editor' ).__unstableGetClientIdWithClientIdsTree", + { + since: '6.3', + version: '6.5', + } + ); + return { + clientId, + innerBlocks: __unstableGetClientIdsTree( state, clientId ), + }; + }, ( state ) => [ state.blocks.order ] ); @@ -205,16 +216,26 @@ export const __unstableGetClientIdWithClientIdsTree = createSelector( * given root, consisting of stripped down block objects containing only * their client IDs, and their inner blocks' client IDs. * + * @deprecated + * * @param {Object} state Editor state. * @param {?string} rootClientId Optional root client ID of block list. * * @return {Object[]} Client IDs of the post blocks. */ export const __unstableGetClientIdsTree = createSelector( - ( state, rootClientId = '' ) => - getBlockOrder( state, rootClientId ).map( ( clientId ) => + ( state, rootClientId = '' ) => { + deprecated( + "wp.data.select( 'core/block-editor' ).__unstableGetClientIdsTree", + { + since: '6.3', + version: '6.5', + } + ); + return getBlockOrder( state, rootClientId ).map( ( clientId ) => __unstableGetClientIdWithClientIdsTree( state, clientId ) - ), + ); + }, ( state ) => [ state.blocks.order ] ); diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index ecae342317ce18..ab0d4b11c5bf40 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -11,6 +11,7 @@ import { getLastInsertedBlocksClientIds, getBlockEditingMode, isBlockSubtreeDisabled, + getListViewClientIdsTree, } from '../private-selectors'; jest.mock( '@wordpress/data/src/select', () => ( { @@ -74,7 +75,13 @@ describe( 'private selectors', () => { [ 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', {} ], // | | Paragraph ] ), order: new Map( [ - [ '', [ '6cf70164-9097-4460-bcbf-200560546988' ] ], + [ + '', + [ + '6cf70164-9097-4460-bcbf-200560546988', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + ], [ '6cf70164-9097-4460-bcbf-200560546988', [] ], [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', @@ -382,4 +389,169 @@ describe( 'private selectors', () => { } ); } ); } ); + + describe( 'getListViewClientIdsTree', () => { + const baseState = { + settings: {}, + blocks: { + byClientId: new Map( [ + [ '6cf70164-9097-4460-bcbf-200560546988', {} ], // Header + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', {} ], // Group + [ 'b26fc763-417d-4f01-b81c-2ec61e14a972', {} ], // | Post Title + [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', {} ], // | Post Content + [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', {} ], // | | Paragraph + [ 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', {} ], // | | Paragraph + ] ), + order: new Map( [ + [ + '', + [ + '6cf70164-9097-4460-bcbf-200560546988', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + ], + [ '6cf70164-9097-4460-bcbf-200560546988', [] ], + [ + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + [ + 'b26fc763-417d-4f01-b81c-2ec61e14a972', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], + ], + [ 'b26fc763-417d-4f01-b81c-2ec61e14a972', [] ], + [ + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + [ + 'b3247f75-fd94-4fef-97f9-5bfd162cc416', + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + ], + ], + [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', [] ], + [ 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', [] ], + ] ), + parents: new Map( [ + [ '6cf70164-9097-4460-bcbf-200560546988', '' ], + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', '' ], + [ + 'b26fc763-417d-4f01-b81c-2ec61e14a972', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + [ + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + [ + 'b3247f75-fd94-4fef-97f9-5bfd162cc416', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], + [ + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], + ] ), + }, + blockListSettings: { + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337': {}, + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f': {}, + }, + }; + + it( 'should return tree containing only clientId and innerBlocks', () => { + const state = { + ...baseState, + blockEditingModes: new Map( [] ), + }; + expect( getListViewClientIdsTree( state ) ).toEqual( [ + { + clientId: '6cf70164-9097-4460-bcbf-200560546988', + innerBlocks: [], + }, + { + clientId: 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + innerBlocks: [ + { + clientId: 'b26fc763-417d-4f01-b81c-2ec61e14a972', + innerBlocks: [], + }, + { + clientId: '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + innerBlocks: [ + { + clientId: + 'b3247f75-fd94-4fef-97f9-5bfd162cc416', + innerBlocks: [], + }, + { + clientId: + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + innerBlocks: [], + }, + ], + }, + ], + }, + ] ); + } ); + + it( 'should return a subtree when rootBlockClientId is given', () => { + const state = { + ...baseState, + blockEditingModes: new Map( [] ), + }; + expect( + getListViewClientIdsTree( + state, + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337' + ) + ).toEqual( [ + { + clientId: 'b26fc763-417d-4f01-b81c-2ec61e14a972', + innerBlocks: [], + }, + { + clientId: '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + innerBlocks: [ + { + clientId: 'b3247f75-fd94-4fef-97f9-5bfd162cc416', + innerBlocks: [], + }, + { + clientId: 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + innerBlocks: [], + }, + ], + }, + ] ); + } ); + + it( 'should filter out disabled blocks', () => { + const state = { + ...baseState, + blockEditingModes: new Map( [ + [ '', 'disabled' ], + [ 'b26fc763-417d-4f01-b81c-2ec61e14a972', 'contentOnly' ], + [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', 'contentOnly' ], + ] ), + }; + expect( getListViewClientIdsTree( state ) ).toEqual( [ + { + clientId: 'b26fc763-417d-4f01-b81c-2ec61e14a972', + innerBlocks: [], + }, + { + clientId: '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + innerBlocks: [ + { + clientId: 'b3247f75-fd94-4fef-97f9-5bfd162cc416', + innerBlocks: [], + }, + { + clientId: 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + innerBlocks: [], + }, + ], + }, + ] ); + } ); + } ); } ); diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 5cc17fc08b3142..dca9b847bc5a2f 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -4734,6 +4734,7 @@ describe( '__unstableGetClientIdWithClientIdsTree', () => { { clientId: 'baz', innerBlocks: [] }, ], } ); + expect( console ).toHaveWarned(); } ); } ); describe( '__unstableGetClientIdsTree', () => { From b5c74388e91d5ccef9dc62ee00743d19f647f8d2 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 19 Jun 2023 14:46:29 +1000 Subject: [PATCH 080/163] Improve guidance to editing template when focused on editing a page (#51366) * Improve guidance to editing template when focused on editing a page * Don't flash content blocks when double clicking * Use flashBlock() * Remove content block flashing for now * Show edit template notification if not already showing one * Oops, this doesn't belong here * Restore packages/block-editor from trunk * Optimise if() * Add periods to notifications * Dismiss notification when opening dialog --- .../src/components/block-editor/index.js | 16 +-- .../block-editor/site-editor-canvas.js | 101 +++++++------- .../back-to-page-notification.js | 65 +++++++++ .../constants.js | 0 .../disable-non-page-content-blocks.js | 2 +- .../edit-template-notification.js | 108 +++++++++++++++ .../page-content-focus-manager/index.js | 26 ++++ .../components/page-content-focus/index.js | 2 - .../use-page-content-focus-notifications.js | 128 ------------------ .../page-panels/page-content.js | 2 +- 10 files changed, 257 insertions(+), 193 deletions(-) create mode 100644 packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js rename packages/edit-site/src/components/{page-content-focus => page-content-focus-manager}/constants.js (100%) rename packages/edit-site/src/components/{page-content-focus => page-content-focus-manager}/disable-non-page-content-blocks.js (96%) create mode 100644 packages/edit-site/src/components/page-content-focus-manager/edit-template-notification.js create mode 100644 packages/edit-site/src/components/page-content-focus-manager/index.js delete mode 100644 packages/edit-site/src/components/page-content-focus/index.js delete mode 100644 packages/edit-site/src/components/page-content-focus/use-page-content-focus-notifications.js diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index 4f4dd2011302f7..fcd28948ccbb3f 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -16,21 +16,14 @@ import { ReusableBlocksMenuItems } from '@wordpress/reusable-blocks'; import TemplatePartConverter from '../template-part-converter'; import { SidebarInspectorFill } from '../sidebar-edit-mode'; import { store as editSiteStore } from '../../store'; -import { unlock } from '../../lock-unlock'; -import { DisableNonPageContentBlocks } from '../page-content-focus'; import SiteEditorCanvas from './site-editor-canvas'; import getBlockEditorProvider from './get-block-editor-provider'; export default function BlockEditor() { - const { entityType, hasPageContentFocus } = useSelect( ( select ) => { - const { getEditedPostType, hasPageContentFocus: _hasPageContentFocus } = - unlock( select( editSiteStore ) ); - - return { - entityType: getEditedPostType(), - hasPageContentFocus: _hasPageContentFocus(), - }; - }, [] ); + const entityType = useSelect( + ( select ) => select( editSiteStore ).getEditedPostType(), + [] + ); // Choose the provider based on the entity type currently // being edited. @@ -38,7 +31,6 @@ export default function BlockEditor() { return ( - { hasPageContentFocus && } diff --git a/packages/edit-site/src/components/block-editor/site-editor-canvas.js b/packages/edit-site/src/components/block-editor/site-editor-canvas.js index 29e980d1134340..ecd6a73f92c353 100644 --- a/packages/edit-site/src/components/block-editor/site-editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/site-editor-canvas.js @@ -27,11 +27,11 @@ import BackButton from './back-button'; import ResizableEditor from './resizable-editor'; import EditorCanvas from './editor-canvas'; import EditorCanvasContainer from '../editor-canvas-container'; -import { usePageContentFocusNotifications } from '../page-content-focus'; import useSiteEditorSettings from './use-site-editor-settings'; import { store as editSiteStore } from '../../store'; import { FOCUSABLE_ENTITIES } from './constants'; import { unlock } from '../../lock-unlock'; +import PageContentFocusManager from '../page-content-focus-manager'; const LAYOUT = { type: 'default', @@ -82,7 +82,6 @@ export default function SiteEditorCanvas() { contentRef, useClipboardHandler(), useTypingObserver(), - usePageContentFocusNotifications(), ] ); const isTemplateTypeNavigation = templateType === 'wp_navigation'; @@ -98,55 +97,59 @@ export default function SiteEditorCanvas() { : undefined; return ( - - { ( [ editorCanvasView ] ) => - editorCanvasView ? ( -
    - { editorCanvasView } -
    - ) : ( - { - // Clear selected block when clicking on the gray background. - if ( event.target === event.currentTarget ) { - clearSelectedBlock(); - } - } } - > - - - + + { ( [ editorCanvasView ] ) => + editorCanvasView ? ( +
    + { editorCanvasView } +
    + ) : ( + { + // Clear selected block when clicking on the gray background. + if ( event.target === event.currentTarget ) { + clearSelectedBlock(); + } + } } > - + + - { resizeObserver } - - -
    -
    - ) - } -
    + + { resizeObserver } + + + + + ) + } + + + ); } diff --git a/packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js b/packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js new file mode 100644 index 00000000000000..171d4b6a5f7579 --- /dev/null +++ b/packages/edit-site/src/components/page-content-focus-manager/back-to-page-notification.js @@ -0,0 +1,65 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect, useRef } from '@wordpress/element'; +import { store as noticesStore } from '@wordpress/notices'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; + +/** + * Component that displays a 'You are editing a template' notification when the + * user switches from focusing on editing page content to editing a template. + */ +export default function BackToPageNotification() { + useBackToPageNotification(); + return null; +} + +/** + * Hook that displays a 'You are editing a template' notification when the user + * switches from focusing on editing page content to editing a template. + */ +export function useBackToPageNotification() { + const hasPageContentFocus = useSelect( + ( select ) => select( editSiteStore ).hasPageContentFocus(), + [] + ); + + const alreadySeen = useRef( false ); + const prevHasPageContentFocus = useRef( false ); + + const { createInfoNotice } = useDispatch( noticesStore ); + const { setHasPageContentFocus } = useDispatch( editSiteStore ); + + useEffect( () => { + if ( + ! alreadySeen.current && + prevHasPageContentFocus.current && + ! hasPageContentFocus + ) { + createInfoNotice( __( 'You are editing a template.' ), { + isDismissible: true, + type: 'snackbar', + actions: [ + { + label: __( 'Back to page' ), + onClick: () => setHasPageContentFocus( true ), + }, + ], + } ); + alreadySeen.current = true; + } + prevHasPageContentFocus.current = hasPageContentFocus; + }, [ + alreadySeen, + prevHasPageContentFocus, + hasPageContentFocus, + createInfoNotice, + setHasPageContentFocus, + ] ); +} diff --git a/packages/edit-site/src/components/page-content-focus/constants.js b/packages/edit-site/src/components/page-content-focus-manager/constants.js similarity index 100% rename from packages/edit-site/src/components/page-content-focus/constants.js rename to packages/edit-site/src/components/page-content-focus-manager/constants.js diff --git a/packages/edit-site/src/components/page-content-focus/disable-non-page-content-blocks.js b/packages/edit-site/src/components/page-content-focus-manager/disable-non-page-content-blocks.js similarity index 96% rename from packages/edit-site/src/components/page-content-focus/disable-non-page-content-blocks.js rename to packages/edit-site/src/components/page-content-focus-manager/disable-non-page-content-blocks.js index 9bd86fb1b4e246..33ea486863d203 100644 --- a/packages/edit-site/src/components/page-content-focus/disable-non-page-content-blocks.js +++ b/packages/edit-site/src/components/page-content-focus-manager/disable-non-page-content-blocks.js @@ -18,7 +18,7 @@ const { useBlockEditingMode } = unlock( blockEditorPrivateApis ); * Component that when rendered, makes it so that the site editor allows only * page content to be edited. */ -export function DisableNonPageContentBlocks() { +export default function DisableNonPageContentBlocks() { useDisableNonPageContentBlocks(); } diff --git a/packages/edit-site/src/components/page-content-focus-manager/edit-template-notification.js b/packages/edit-site/src/components/page-content-focus-manager/edit-template-notification.js new file mode 100644 index 00000000000000..3518bc8c3c51dc --- /dev/null +++ b/packages/edit-site/src/components/page-content-focus-manager/edit-template-notification.js @@ -0,0 +1,108 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect, useState, useRef } from '@wordpress/element'; +import { store as noticesStore } from '@wordpress/notices'; +import { __ } from '@wordpress/i18n'; +import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; + +/** + * Component that: + * + * - Displays a 'Edit your template to edit this block' notification when the + * user is focusing on editing page content and clicks on a disabled template + * block. + * - Displays a 'Edit your template to edit this block' dialog when the user + * is focusing on editing page conetnt and double clicks on a disabled + * template block. + * + * @param {Object} props + * @param {import('react').RefObject} props.contentRef Ref to the block + * editor iframe canvas. + */ +export default function EditTemplateNotification( { contentRef } ) { + const hasPageContentFocus = useSelect( + ( select ) => select( editSiteStore ).hasPageContentFocus(), + [] + ); + const { getNotices } = useSelect( noticesStore ); + + const { createInfoNotice, removeNotice } = useDispatch( noticesStore ); + const { setHasPageContentFocus } = useDispatch( editSiteStore ); + + const [ isDialogOpen, setIsDialogOpen ] = useState( false ); + + const lastNoticeId = useRef( 0 ); + + useEffect( () => { + const handleClick = async ( event ) => { + if ( ! hasPageContentFocus ) { + return; + } + if ( ! event.target.classList.contains( 'is-root-container' ) ) { + return; + } + const isNoticeAlreadyShowing = getNotices().some( + ( notice ) => notice.id === lastNoticeId.current + ); + if ( isNoticeAlreadyShowing ) { + return; + } + const { notice } = await createInfoNotice( + __( 'Edit your template to edit this block.' ), + { + isDismissible: true, + type: 'snackbar', + actions: [ + { + label: __( 'Edit template' ), + onClick: () => setHasPageContentFocus( false ), + }, + ], + } + ); + lastNoticeId.current = notice.id; + }; + + const handleDblClick = ( event ) => { + if ( ! hasPageContentFocus ) { + return; + } + if ( ! event.target.classList.contains( 'is-root-container' ) ) { + return; + } + if ( lastNoticeId.current ) { + removeNotice( lastNoticeId.current ); + } + setIsDialogOpen( true ); + }; + + const canvas = contentRef.current; + canvas?.addEventListener( 'click', handleClick ); + canvas?.addEventListener( 'dblclick', handleDblClick ); + return () => { + canvas?.removeEventListener( 'click', handleClick ); + canvas?.removeEventListener( 'dblclick', handleDblClick ); + }; + }, [ lastNoticeId, hasPageContentFocus, contentRef.current ] ); + + return ( + { + setIsDialogOpen( false ); + setHasPageContentFocus( false ); + } } + onCancel={ () => setIsDialogOpen( false ) } + > + { __( 'Edit your template to edit this block.' ) } + + ); +} diff --git a/packages/edit-site/src/components/page-content-focus-manager/index.js b/packages/edit-site/src/components/page-content-focus-manager/index.js new file mode 100644 index 00000000000000..935ba1c96248cf --- /dev/null +++ b/packages/edit-site/src/components/page-content-focus-manager/index.js @@ -0,0 +1,26 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; +import DisableNonPageContentBlocks from './disable-non-page-content-blocks'; +import EditTemplateNotification from './edit-template-notification'; +import BackToPageNotification from './back-to-page-notification'; + +export default function PageContentFocusManager( { contentRef } ) { + const hasPageContentFocus = useSelect( + ( select ) => select( editSiteStore ).hasPageContentFocus(), + [] + ); + return ( + <> + { hasPageContentFocus && } + + + + ); +} diff --git a/packages/edit-site/src/components/page-content-focus/index.js b/packages/edit-site/src/components/page-content-focus/index.js deleted file mode 100644 index 81160f4a861f93..00000000000000 --- a/packages/edit-site/src/components/page-content-focus/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export * from './disable-non-page-content-blocks'; -export { usePageContentFocusNotifications } from './use-page-content-focus-notifications'; diff --git a/packages/edit-site/src/components/page-content-focus/use-page-content-focus-notifications.js b/packages/edit-site/src/components/page-content-focus/use-page-content-focus-notifications.js deleted file mode 100644 index a1ebc729294d53..00000000000000 --- a/packages/edit-site/src/components/page-content-focus/use-page-content-focus-notifications.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect, useDispatch } from '@wordpress/data'; -import { useEffect, useRef } from '@wordpress/element'; -import { store as noticesStore } from '@wordpress/notices'; -import { __ } from '@wordpress/i18n'; -import { useRefEffect } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import { store as editSiteStore } from '../../store'; - -/** - * Hook that displays notifications that guide the user towards using the - * content vs. template editing modes. - * - * @return {import('react').RefObject} Ref which should be passed - * (using useMergeRefs()) to - * the editor iframe canvas. - */ -export function usePageContentFocusNotifications() { - const ref = useEditTemplateNotification(); - useBackToPageNotification(); - return ref; -} - -/** - * Hook that displays a 'Edit your template to edit this block' notification - * when the user is focusing on editing page content and clicks on a disabled - * template block. - * - * @return {import('react').RefObject} Ref which should be passed - * (using useMergeRefs()) to - * the editor iframe canvas. - */ -function useEditTemplateNotification() { - const hasPageContentFocus = useSelect( - ( select ) => select( editSiteStore ).hasPageContentFocus(), - [] - ); - - const alreadySeen = useRef( false ); - - const { createInfoNotice } = useDispatch( noticesStore ); - const { setHasPageContentFocus } = useDispatch( editSiteStore ); - - return useRefEffect( - ( node ) => { - const handleClick = ( event ) => { - if ( - ! alreadySeen.current && - hasPageContentFocus && - event.target.classList.contains( 'is-root-container' ) - ) { - createInfoNotice( - __( 'Edit your template to edit this block' ), - { - isDismissible: true, - type: 'snackbar', - actions: [ - { - label: __( 'Edit template' ), - onClick: () => - setHasPageContentFocus( false ), - }, - ], - } - ); - alreadySeen.current = true; - } - }; - node.addEventListener( 'click', handleClick ); - return () => node.removeEventListener( 'click', handleClick ); - }, - [ - hasPageContentFocus, - alreadySeen, - createInfoNotice, - setHasPageContentFocus, - ] - ); -} - -/** - * Hook that displays a 'You are editing a template' notification when the user - * switches from focusing on editing page content to editing a template. - */ -function useBackToPageNotification() { - const hasPageContentFocus = useSelect( - ( select ) => select( editSiteStore ).hasPageContentFocus(), - [] - ); - - const alreadySeen = useRef( false ); - const prevHasPageContentFocus = useRef( false ); - - const { createInfoNotice } = useDispatch( noticesStore ); - const { setHasPageContentFocus } = useDispatch( editSiteStore ); - - useEffect( () => { - if ( - ! alreadySeen.current && - prevHasPageContentFocus.current && - ! hasPageContentFocus - ) { - createInfoNotice( __( 'You are editing a template' ), { - isDismissible: true, - type: 'snackbar', - actions: [ - { - label: __( 'Back to page' ), - onClick: () => setHasPageContentFocus( true ), - }, - ], - } ); - alreadySeen.current = true; - } - prevHasPageContentFocus.current = hasPageContentFocus; - }, [ - alreadySeen, - prevHasPageContentFocus, - hasPageContentFocus, - createInfoNotice, - setHasPageContentFocus, - ] ); -} diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js index 301f898d794a4b..d6e7dd23a709fa 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js @@ -10,7 +10,7 @@ import { /** * Internal dependencies */ -import { PAGE_CONTENT_BLOCK_TYPES } from '../../page-content-focus/constants'; +import { PAGE_CONTENT_BLOCK_TYPES } from '../../page-content-focus-manager/constants'; import { unlock } from '../../../lock-unlock'; const { BlockQuickNavigation } = unlock( blockEditorPrivateApis ); From 6e9423345620dc4ca0d553fc51df778dbdc877bb Mon Sep 17 00:00:00 2001 From: Aurooba Ahmed Date: Sun, 18 Jun 2023 23:46:51 -0600 Subject: [PATCH 081/163] remove `accordion` from details block keywords (#51597) --- packages/block-library/src/details/block.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/details/block.json b/packages/block-library/src/details/block.json index f3a0c00defdc31..01110dfec26ff2 100644 --- a/packages/block-library/src/details/block.json +++ b/packages/block-library/src/details/block.json @@ -5,7 +5,7 @@ "title": "Details", "category": "text", "description": "Hide and show additional content.", - "keywords": [ "disclosure", "summary", "hide", "accordion" ], + "keywords": [ "disclosure", "summary", "hide" ], "textdomain": "default", "attributes": { "showContent": { From 7997bc31dc2ccddb5f5e2e5f76137a1628fbf033 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:41:55 +0900 Subject: [PATCH 082/163] Docs: Fix incorrect import of useEntityRecords in code example (#51630) --- packages/core-data/README.md | 2 +- packages/core-data/src/hooks/use-entity-records.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-data/README.md b/packages/core-data/README.md index d2ac90a4a5165b..ade2293efe167a 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -859,7 +859,7 @@ Resolves the specified entity records. _Usage_ ```js -import { useEntityRecord } from '@wordpress/core-data'; +import { useEntityRecords } from '@wordpress/core-data'; function PageTitlesList() { const { records, isResolving } = useEntityRecords( 'postType', 'page' ); diff --git a/packages/core-data/src/hooks/use-entity-records.ts b/packages/core-data/src/hooks/use-entity-records.ts index 01d4aa9e7e6091..ff72ea4078c0e4 100644 --- a/packages/core-data/src/hooks/use-entity-records.ts +++ b/packages/core-data/src/hooks/use-entity-records.ts @@ -43,7 +43,7 @@ const EMPTY_ARRAY = []; * @param options Optional hook options. * @example * ```js - * import { useEntityRecord } from '@wordpress/core-data'; + * import { useEntityRecords } from '@wordpress/core-data'; * * function PageTitlesList() { * const { records, isResolving } = useEntityRecords( 'postType', 'page' ); From b13519b25867fd4cf13c29722ec3e3ea9f9b16b9 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:42:08 +0900 Subject: [PATCH 083/163] Search Block: Change Button Only label to sentence case (#51629) --- packages/block-library/src/search/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index bbc864e035e9de..7cfa293076b3d1 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -226,7 +226,7 @@ export default function SearchEdit( { }, { role: 'menuitemradio', - title: __( 'Button Only' ), + title: __( 'Button only' ), isActive: buttonPosition === 'button-only', icon: buttonOnly, onClick: () => { From 69ad3d4a177b0110a61baec0d5aa9838d441f849 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 19 Jun 2023 10:59:19 +0400 Subject: [PATCH 084/163] Update the 'Iframe block' test and fix flakiness (#51631) --- ...and-dependencies-in-iframe-1-chromium.txt} | 0 .../editor/plugins/iframed-block.spec.js | 27 +++---------------- 2 files changed, 4 insertions(+), 23 deletions(-) rename test/e2e/specs/editor/plugins/__snapshots__/{Iframed-block-Should-save-the-changes-1-chromium.txt => Iframed-block-should-load-script-and-dependencies-in-iframe-1-chromium.txt} (100%) diff --git a/test/e2e/specs/editor/plugins/__snapshots__/Iframed-block-Should-save-the-changes-1-chromium.txt b/test/e2e/specs/editor/plugins/__snapshots__/Iframed-block-should-load-script-and-dependencies-in-iframe-1-chromium.txt similarity index 100% rename from test/e2e/specs/editor/plugins/__snapshots__/Iframed-block-Should-save-the-changes-1-chromium.txt rename to test/e2e/specs/editor/plugins/__snapshots__/Iframed-block-should-load-script-and-dependencies-in-iframe-1-chromium.txt diff --git a/test/e2e/specs/editor/plugins/iframed-block.spec.js b/test/e2e/specs/editor/plugins/iframed-block.spec.js index c5529d9108e9f6..0b5343169d5bf8 100644 --- a/test/e2e/specs/editor/plugins/iframed-block.spec.js +++ b/test/e2e/specs/editor/plugins/iframed-block.spec.js @@ -13,36 +13,17 @@ test.describe( 'Iframed block', () => { await requestUtils.deactivatePlugin( 'gutenberg-test-iframed-block' ); } ); - test( 'Should save the changes', async ( { editor, page } ) => { + test( 'should load script and dependencies in iframe', async ( { + editor, + } ) => { await editor.insertBlock( { name: 'test/iframed-block' } ); expect( await editor.getEditedPostContent() ).toMatchSnapshot(); + // Expect the script to load in the iframe, which replaces the block text. await expect( editor.canvas.locator( 'role=document[name="Block: Iframed Block"i]' ) ).toContainText( 'Iframed Block (set with jQuery)' ); - - // open page from sidebar settings - await page.click( - 'role=region[name="Editor settings"i] >> role=button[name="Page"i]' - ); - - // Opens the template editor with a newly created template. - await page.click( 'role=button[name="Select template"i]' ); - await page.click( 'role=button[name="Add template"i]' ); - await page.fill( 'role=textbox[name="NAME"i]', 'Test template' ); - await page.click( 'role=button[name="Create"i]' ); - - // Expect iframe canvas to be visible - await expect( - page.locator( 'iframe[name="editor-canvas"]' ) - ).toBeVisible(); - - // Expect the script to load in the iframe, which replaces the block text. - const iframedText = page.frameLocator( 'iframe' ).locator( 'body' ); - await expect( iframedText ).toContainText( - 'Iframed Block (set with jQuery)' - ); } ); } ); From 635484bb5acc6a00333181a253d64d9644a0fea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Walb=C3=B8=20Johnsg=C3=A5rd?= Date: Mon, 19 Jun 2023 09:11:15 +0200 Subject: [PATCH 085/163] Inserter: Fix arrows in RTL mode (#51622) --- .../src/components/inserter/block-patterns-tab.js | 12 +++++++++--- .../src/components/inserter/media-tab/media-tab.js | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab.js b/packages/block-editor/src/components/inserter/block-patterns-tab.js index dfa0676eefb6fe..49a5939107bb1f 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab.js @@ -8,7 +8,7 @@ import { useRef, useEffect, } from '@wordpress/element'; -import { _x, __ } from '@wordpress/i18n'; +import { _x, __, isRTL } from '@wordpress/i18n'; import { useAsyncList, useViewportMatch } from '@wordpress/compose'; import { __experimentalItemGroup as ItemGroup, @@ -17,7 +17,7 @@ import { FlexBlock, Button, } from '@wordpress/components'; -import { Icon, chevronRight } from '@wordpress/icons'; +import { Icon, chevronRight, chevronLeft } from '@wordpress/icons'; import { focus } from '@wordpress/dom'; /** @@ -240,7 +240,13 @@ function BlockPatternsTabs( { { category.label } - + ) ) } diff --git a/packages/block-editor/src/components/inserter/media-tab/media-tab.js b/packages/block-editor/src/components/inserter/media-tab/media-tab.js index b84b5aa68b2dff..b448ae2a406499 100644 --- a/packages/block-editor/src/components/inserter/media-tab/media-tab.js +++ b/packages/block-editor/src/components/inserter/media-tab/media-tab.js @@ -6,7 +6,7 @@ import classNames from 'classnames'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { useViewportMatch } from '@wordpress/compose'; import { __experimentalItemGroup as ItemGroup, @@ -16,7 +16,7 @@ import { Button, } from '@wordpress/components'; import { useCallback, useMemo } from '@wordpress/element'; -import { Icon, chevronRight } from '@wordpress/icons'; +import { Icon, chevronRight, chevronLeft } from '@wordpress/icons'; /** * Internal dependencies @@ -89,7 +89,13 @@ function MediaTab( { { mediaCategory.labels.name } - + ) ) } From 783bb9c65360ec5167a412f7aed75e852133074c Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 19 Jun 2023 12:34:00 +0400 Subject: [PATCH 086/163] Fix flaky 'Push to Global Styles' e2e test (#51636) * Fix flaky 'Push to Global Styles' e2e test * Simplify beforeAll --- .../site-editor/push-to-global-styles.spec.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/e2e/specs/site-editor/push-to-global-styles.spec.js b/test/e2e/specs/site-editor/push-to-global-styles.spec.js index 4f51cbd88aad67..937b2aea0fb454 100644 --- a/test/e2e/specs/site-editor/push-to-global-styles.spec.js +++ b/test/e2e/specs/site-editor/push-to-global-styles.spec.js @@ -5,11 +5,7 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); test.describe( 'Push to Global Styles button', () => { test.beforeAll( async ( { requestUtils } ) => { - await Promise.all( [ - requestUtils.activateTheme( 'emptytheme' ), - requestUtils.deleteAllTemplates( 'wp_template' ), - requestUtils.deleteAllTemplates( 'wp_template_part' ), - ] ); + await requestUtils.activateTheme( 'emptytheme' ); } ); test.afterAll( async ( { requestUtils } ) => { @@ -17,7 +13,10 @@ test.describe( 'Push to Global Styles button', () => { } ); test.beforeEach( async ( { admin, editor } ) => { - await admin.visitSiteEditor(); + await admin.visitSiteEditor( { + postId: 'emptytheme//index', + postType: 'wp_template', + } ); await editor.canvas.click( 'body' ); } ); @@ -29,8 +28,10 @@ test.describe( 'Push to Global Styles button', () => { await editor.insertBlock( { name: 'core/heading' } ); await page.keyboard.type( 'A heading' ); + const topBar = page.getByRole( 'region', { name: 'Editor top bar' } ); + // Navigate to Styles -> Blocks -> Heading -> Typography - await page.getByRole( 'button', { name: 'Styles' } ).click(); + await topBar.getByRole( 'button', { name: 'Styles' } ).click(); await page.getByRole( 'button', { name: 'Blocks styles' } ).click(); await page .getByRole( 'button', { name: 'Heading block styles' } ) @@ -42,7 +43,7 @@ test.describe( 'Push to Global Styles button', () => { ).toHaveAttribute( 'aria-pressed', 'false' ); // Go to block settings and open the Advanced panel - await page.getByRole( 'button', { name: 'Settings' } ).click(); + await topBar.getByRole( 'button', { name: 'Settings' } ).click(); await page.getByRole( 'button', { name: 'Advanced' } ).click(); // Push button should be disabled From a538973fb340d21a454aee2dabd6f689b012d9df Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 19 Jun 2023 12:34:19 +0400 Subject: [PATCH 087/163] Combine Site Editor list view tests into a single file (#51635) --- .../block-list-panel-preference.spec.js | 49 ------------------- test/e2e/specs/site-editor/list-view.spec.js | 28 +++++++++++ 2 files changed, 28 insertions(+), 49 deletions(-) delete mode 100644 test/e2e/specs/site-editor/block-list-panel-preference.spec.js diff --git a/test/e2e/specs/site-editor/block-list-panel-preference.spec.js b/test/e2e/specs/site-editor/block-list-panel-preference.spec.js deleted file mode 100644 index 7a6e83d09cf7ca..00000000000000 --- a/test/e2e/specs/site-editor/block-list-panel-preference.spec.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * WordPress dependencies - */ -const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); - -test.describe( 'Block list view', () => { - test.beforeAll( async ( { requestUtils } ) => { - await requestUtils.activateTheme( 'emptytheme' ); - } ); - - test.afterAll( async ( { requestUtils } ) => { - await requestUtils.activateTheme( 'twentytwentyone' ); - } ); - - test( 'Should open by default', async ( { admin, page, editor } ) => { - await admin.visitSiteEditor( { - postId: 'emptytheme//index', - postType: 'wp_template', - } ); - - await editor.canvas.click( 'body' ); - - // Should display the Preview button. - await expect( - page.locator( 'role=region[name="List View"i]' ) - ).not.toBeVisible(); - - // Turn on block list view by default. - await page.evaluate( () => { - window.wp.data - .dispatch( 'core/preferences' ) - .set( 'core/edit-site', 'showListViewByDefault', true ); - } ); - - await page.reload(); - - // Should display the Preview button. - await expect( - page.locator( 'role=region[name="List View"i]' ) - ).toBeVisible(); - - // The preferences cleanup. - await page.evaluate( () => { - window.wp.data - .dispatch( 'core/preferences' ) - .set( 'core/edit-site', 'showListViewByDefault', false ); - } ); - } ); -} ); diff --git a/test/e2e/specs/site-editor/list-view.spec.js b/test/e2e/specs/site-editor/list-view.spec.js index 2cecc34950dd0a..94bac47d0ecf58 100644 --- a/test/e2e/specs/site-editor/list-view.spec.js +++ b/test/e2e/specs/site-editor/list-view.spec.js @@ -21,6 +21,34 @@ test.describe( 'Site Editor List View', () => { await editor.canvas.click( 'body' ); } ); + test( 'should open by default when preference is enabled', async ( { + page, + } ) => { + await expect( + page.locator( 'role=region[name="List View"i]' ) + ).toBeHidden(); + + // Turn on block list view by default. + await page.evaluate( () => { + window.wp.data + .dispatch( 'core/preferences' ) + .set( 'core/edit-site', 'showListViewByDefault', true ); + } ); + + await page.reload(); + + await expect( + page.locator( 'role=region[name="List View"i]' ) + ).toBeVisible(); + + // The preferences cleanup. + await page.evaluate( () => { + window.wp.data + .dispatch( 'core/preferences' ) + .set( 'core/edit-site', 'showListViewByDefault', false ); + } ); + } ); + // If list view sidebar is open and focus is not inside the sidebar, move // focus to the sidebar when using the shortcut. If focus is inside the // sidebar, shortcut should close the sidebar. From 16ba6c38634869542f3ca1be9fc3e1e0e885a9bf Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 19 Jun 2023 12:53:50 +0400 Subject: [PATCH 088/163] Details: Set 'clientId' as useSelect dependency (#51634) --- packages/block-library/src/details/edit.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/details/edit.js b/packages/block-library/src/details/edit.js index bc0c206aa2d26d..81e4d7a52056a7 100644 --- a/packages/block-library/src/details/edit.js +++ b/packages/block-library/src/details/edit.js @@ -30,15 +30,18 @@ function DetailsEdit( { attributes, setAttributes, clientId } ) { } ); // Check if either the block or the inner blocks are selected. - const hasSelection = useSelect( ( select ) => { - const { isBlockSelected, hasSelectedInnerBlock } = - select( blockEditorStore ); - /* Sets deep to true to also find blocks inside the details content block. */ - return ( - hasSelectedInnerBlock( clientId, true ) || - isBlockSelected( clientId ) - ); - }, [] ); + const hasSelection = useSelect( + ( select ) => { + const { isBlockSelected, hasSelectedInnerBlock } = + select( blockEditorStore ); + /* Sets deep to true to also find blocks inside the details content block. */ + return ( + hasSelectedInnerBlock( clientId, true ) || + isBlockSelected( clientId ) + ); + }, + [ clientId ] + ); return ( <> From 05296283061191035bc120f2a46a6042f15074ed Mon Sep 17 00:00:00 2001 From: Juan Aldasoro Date: Mon, 19 Jun 2023 10:56:59 +0200 Subject: [PATCH 089/163] Style Book: Close list view when opening the style book. (#50438) * Close list view when opening the style book. * Respect the preference for showing the list view by default when toggling the style book. * Set canvas mode to edit on close. * Use `get` instead of `isFeatureActive` which is deprecated. * Apply suggestions from code review. * Force closing list view when opening stylebook from site navigation. * Close the list view when opening the revisions. --- .../editor-canvas-container/index.js | 23 +++++++- .../src/components/global-styles/ui.js | 2 + .../global-styles-sidebar.js | 59 +++++++++++-------- .../index.js | 10 +++- 4 files changed, 65 insertions(+), 29 deletions(-) diff --git a/packages/edit-site/src/components/editor-canvas-container/index.js b/packages/edit-site/src/components/editor-canvas-container/index.js index c28a565e960959..c036374e7907e1 100644 --- a/packages/edit-site/src/components/editor-canvas-container/index.js +++ b/packages/edit-site/src/components/editor-canvas-container/index.js @@ -12,6 +12,7 @@ import { __ } from '@wordpress/i18n'; import { useDispatch, useSelect } from '@wordpress/data'; import { closeSmall } from '@wordpress/icons'; import { useFocusOnMount, useFocusReturn } from '@wordpress/compose'; +import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies @@ -53,9 +54,22 @@ function EditorCanvasContainer( { onClose, enableResizing = false, } ) { - const editorCanvasContainerView = useSelect( - ( select ) => - unlock( select( editSiteStore ) ).getEditorCanvasContainerView(), + const { editorCanvasContainerView, showListViewByDefault } = useSelect( + ( select ) => { + const _editorCanvasContainerView = unlock( + select( editSiteStore ) + ).getEditorCanvasContainerView(); + + const _showListViewByDefault = select( preferencesStore ).get( + 'core/edit-site', + 'showListViewByDefault' + ); + + return { + editorCanvasContainerView: _editorCanvasContainerView, + showListViewByDefault: _showListViewByDefault, + }; + }, [] ); const [ isClosed, setIsClosed ] = useState( false ); @@ -69,10 +83,13 @@ function EditorCanvasContainer( { [ editorCanvasContainerView ] ); + const { setIsListViewOpened } = useDispatch( editSiteStore ); + function onCloseContainer() { if ( typeof onClose === 'function' ) { onClose(); } + setIsListViewOpened( showListViewByDefault ); setEditorCanvasContainerView( undefined ); setIsClosed( true ); } diff --git a/packages/edit-site/src/components/global-styles/ui.js b/packages/edit-site/src/components/global-styles/ui.js index 218665c682def4..bd73785e4496cd 100644 --- a/packages/edit-site/src/components/global-styles/ui.js +++ b/packages/edit-site/src/components/global-styles/ui.js @@ -47,6 +47,7 @@ const { Slot: GlobalStylesMenuSlot, Fill: GlobalStylesMenuFill } = function GlobalStylesActionMenu() { const { toggle } = useDispatch( preferencesStore ); + const { setIsListViewOpened } = useDispatch( editSiteStore ); const { canEditCSS, revisionsCount } = useSelect( ( select ) => { const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } = select( coreStore ); @@ -71,6 +72,7 @@ function GlobalStylesActionMenu() { ); const loadCustomCSS = () => goTo( '/css' ); const loadRevisions = () => { + setIsListViewOpened( false ); goTo( '/revisions' ); setEditorCanvasContainerView( 'global-styles-revisions' ); }; diff --git a/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js b/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js index b32e849d6a7541..f6e624b4b3f00b 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/global-styles-sidebar.js @@ -7,6 +7,7 @@ import { styles, seen } from '@wordpress/icons'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; import { store as interfaceStore } from '@wordpress/interface'; +import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies @@ -18,28 +19,33 @@ import { GlobalStylesMenuSlot } from '../global-styles/ui'; import { unlock } from '../../lock-unlock'; export default function GlobalStylesSidebar() { - const { shouldClearCanvasContainerView, isStyleBookOpened } = useSelect( - ( select ) => { - const { getActiveComplementaryArea } = select( interfaceStore ); - const { getEditorCanvasContainerView, getCanvasMode } = unlock( - select( editSiteStore ) - ); - const _isVisualEditorMode = - 'visual' === select( editSiteStore ).getEditorMode(); - const _isEditCanvasMode = 'edit' === getCanvasMode(); + const { + shouldClearCanvasContainerView, + isStyleBookOpened, + showListViewByDefault, + } = useSelect( ( select ) => { + const { getActiveComplementaryArea } = select( interfaceStore ); + const { getEditorCanvasContainerView, getCanvasMode } = unlock( + select( editSiteStore ) + ); + const _isVisualEditorMode = + 'visual' === select( editSiteStore ).getEditorMode(); + const _isEditCanvasMode = 'edit' === getCanvasMode(); + const _showListViewByDefault = select( preferencesStore ).get( + 'core/edit-site', + 'showListViewByDefault' + ); - return { - isStyleBookOpened: - 'style-book' === getEditorCanvasContainerView(), - shouldClearCanvasContainerView: - 'edit-site/global-styles' !== - getActiveComplementaryArea( 'core/edit-site' ) || - ! _isVisualEditorMode || - ! _isEditCanvasMode, - }; - }, - [] - ); + return { + isStyleBookOpened: 'style-book' === getEditorCanvasContainerView(), + shouldClearCanvasContainerView: + 'edit-site/global-styles' !== + getActiveComplementaryArea( 'core/edit-site' ) || + ! _isVisualEditorMode || + ! _isEditCanvasMode, + showListViewByDefault: _showListViewByDefault, + }; + }, [] ); const { setEditorCanvasContainerView } = unlock( useDispatch( editSiteStore ) ); @@ -50,6 +56,8 @@ export default function GlobalStylesSidebar() { } }, [ shouldClearCanvasContainerView ] ); + const { setIsListViewOpened } = useDispatch( editSiteStore ); + return ( + onClick={ () => { + setIsListViewOpened( + isStyleBookOpened && showListViewByDefault + ); setEditorCanvasContainerView( isStyleBookOpened ? undefined : 'style-book' - ) - } + ); + } } /> diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js index 04fd4c560e6f9c..c78f77782f0ffd 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js @@ -141,7 +141,8 @@ function SidebarNavigationScreenGlobalStylesFooter( { onClickRevisions } ) { } export default function SidebarNavigationScreenGlobalStyles() { - const { openGeneralSidebar } = useDispatch( editSiteStore ); + const { openGeneralSidebar, setIsListViewOpened } = + useDispatch( editSiteStore ); const isMobileViewport = useViewportMatch( 'medium', '<' ); const { setCanvasMode, setEditorCanvasContainerView } = unlock( useDispatch( editSiteStore ) @@ -169,7 +170,12 @@ export default function SidebarNavigationScreenGlobalStyles() { // and the global styles sidebar is open. This ensures that // the Style Book is not prematurely closed. setEditorCanvasContainerView( 'style-book' ); - }, [ openGlobalStyles, setEditorCanvasContainerView ] ); + setIsListViewOpened( false ); + }, [ + openGlobalStyles, + setEditorCanvasContainerView, + setIsListViewOpened, + ] ); const openRevisions = useCallback( async () => { await openGlobalStyles(); From ac6580d92208b204ea8db5f872f6487816d0232e Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 19 Jun 2023 18:52:34 +0900 Subject: [PATCH 090/163] Site Tagline Block: Remove unnecessary square path from block icon SVG (#51611) --- packages/block-library/src/site-tagline/icon.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-library/src/site-tagline/icon.js b/packages/block-library/src/site-tagline/icon.js index 9e708e94fafd79..262b980c1ce111 100644 --- a/packages/block-library/src/site-tagline/icon.js +++ b/packages/block-library/src/site-tagline/icon.js @@ -5,7 +5,6 @@ import { SVG, Path } from '@wordpress/components'; export default ( - ); From cfaecf2036a54af1b8f1c3b455f50be24c6002b7 Mon Sep 17 00:00:00 2001 From: Sam Najian Date: Mon, 19 Jun 2023 12:02:21 +0200 Subject: [PATCH 091/163] Accept transforms in `gutenberg_get_global_styles` (#50484) --- lib/class-wp-theme-json-gutenberg.php | 74 +++++++++++++ lib/global-styles-and-settings.php | 15 ++- phpunit/class-wp-theme-json-test.php | 154 ++++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 2e372fc32c1702..7313afeba91bca 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -3646,4 +3646,78 @@ private static function resolve_custom_css_format( $tree ) { return $tree; } + + /** + * Replaces CSS variables with their values in place. + * + * @since 6.3.0 + * @param array $styles CSS declarations to convert. + * @param array $values key => value pairs to use for replacement. + * @return array + */ + private static function convert_variables_to_value( $styles, $values ) { + foreach ( $styles as $key => $style ) { + if ( is_array( $style ) ) { + $styles[ $key ] = self::convert_variables_to_value( $style, $values ); + continue; + } + + if ( 0 <= strpos( $style, 'var(' ) ) { + // find all the variables in the string in the form of var(--variable-name, fallback), with fallback in the second capture group. + + $has_matches = preg_match_all( '/var\(([^),]+)?,?\s?(\S+)?\)/', $style, $var_parts ); + + if ( $has_matches ) { + $resolved_style = $styles[ $key ]; + foreach ( $var_parts[1] as $index => $var_part ) { + $key_in_values = 'var(' . $var_part . ')'; + $rule_to_replace = $var_parts[0][ $index ]; // the css rule to replace e.g. var(--wp--preset--color--vivid-green-cyan). + $fallback = $var_parts[2][ $index ]; // the fallback value. + $resolved_style = str_replace( + array( + $rule_to_replace, + $fallback, + ), + array( + isset( $values[ $key_in_values ] ) ? $values[ $key_in_values ] : $rule_to_replace, + isset( $values[ $fallback ] ) ? $values[ $fallback ] : $fallback, + ), + $resolved_style + ); + } + $styles[ $key ] = $resolved_style; + } + } + } + + return $styles; + } + + /** + * Resolves the values of CSS variables in the given styles. + * + * @since 6.3.0 + * @param WP_Theme_JSON_Gutenberg $theme_json The theme json resolver. + * + * @return WP_Theme_JSON_Gutenberg The $theme_json with resolved variables. + */ + public static function resolve_variables( $theme_json ) { + $settings = $theme_json->get_settings(); + $styles = $theme_json->get_raw_data()['styles']; + $preset_vars = static::compute_preset_vars( $settings, static::VALID_ORIGINS ); + $theme_vars = static::compute_theme_vars( $settings ); + $vars = array_reduce( + array_merge( $preset_vars, $theme_vars ), + function( $carry, $item ) { + $name = $item['name']; + $carry[ "var({$name})" ] = $item['value']; + return $carry; + }, + array() + ); + + $theme_json->theme_json['styles'] = self::convert_variables_to_value( $styles, $vars ); + return $theme_json; + } + } diff --git a/lib/global-styles-and-settings.php b/lib/global-styles-and-settings.php index 309f01ca2bdd31..35ff4e13d61a75 100644 --- a/lib/global-styles-and-settings.php +++ b/lib/global-styles-and-settings.php @@ -238,6 +238,8 @@ function _gutenberg_clean_theme_json_caches() { * @since 5.9.0 * @since 6.3.0 the internal link format "var:preset|color|secondary" is resolved * to "var(--wp--preset--font-size--small)" so consumers don't have to. + * @since 6.3.0 `transforms` is now usable in the `context` parameter. In case [`transforms`]['resolve_variables'] + * is defined, variables are resolved to their value in the styles. * * @param array $path Path to the specific style to retrieve. Optional. * If empty, will return all styles. @@ -249,6 +251,9 @@ function _gutenberg_clean_theme_json_caches() { * @type string $origin Which origin to take data from. * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). * If empty or unknown, 'all' is used. + * @type array $transforms Which transformation(s) to apply. + * Valid value is array( 'resolve-variables' ). + * If defined, variables are resolved to their value in the styles. * } * @return array The styles to retrieve. */ @@ -261,8 +266,16 @@ function gutenberg_get_global_styles( $path = array(), $context = array() ) { if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) { $origin = 'theme'; } - $styles = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin )->get_raw_data()['styles']; + $resolve_variables = isset( $context['transforms'] ) + && is_array( $context['transforms'] ) + && in_array( 'resolve-variables', $context['transforms'], true ); + + $merged_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin ); + if ( $resolve_variables ) { + $merged_data = WP_Theme_JSON_Gutenberg::resolve_variables( $merged_data ); + } + $styles = $merged_data->get_raw_data()['styles']; return _wp_array_get( $styles, $path, $styles ); } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 1e5147bd0ed632..bd1d578a1297b6 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -2021,4 +2021,158 @@ public function test_internal_syntax_is_converted_to_css_variables() { $this->assertEquals( 'var(--wp--preset--color--s)', $styles['blocks']['core/quote']['variations']['plain']['color']['background'], 'Style variations: Assert the internal variables are convert to CSS custom variables.' ); } + + public function test_resolve_variables() { + $primary_color = '#9DFF20'; + $secondary_color = '#9DFF21'; + $contrast_color = '#000'; + $raw_color_value = '#efefef'; + $large_font = '18px'; + $small_font = '12px'; + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'color' => array( + 'palette' => array( + 'theme' => array( + array( + 'color' => $primary_color, + 'name' => 'Primary', + 'slug' => 'primary', + ), + array( + 'color' => $secondary_color, + 'name' => 'Secondary', + 'slug' => 'secondary', + ), + array( + 'color' => $contrast_color, + 'name' => 'Contrast', + 'slug' => 'contrast', + ), + ), + ), + ), + 'typography' => array( + 'fontSizes' => array( + array( + 'size' => $small_font, + 'name' => 'Font size small', + 'slug' => 'small', + ), + array( + 'size' => $large_font, + 'name' => 'Font size large', + 'slug' => 'large', + ), + ), + ), + ), + 'styles' => array( + 'color' => array( + 'background' => 'var(--wp--preset--color--primary)', + 'text' => $raw_color_value, + ), + 'elements' => array( + 'button' => array( + 'color' => array( + 'text' => 'var(--wp--preset--color--contrast)', + ), + 'typography' => array( + 'fontSize' => 'var(--wp--preset--font-size--small)', + ), + ), + ), + 'blocks' => array( + 'core/post-terms' => array( + 'typography' => array( 'fontSize' => 'var(--wp--preset--font-size--small)' ), + 'color' => array( 'background' => $raw_color_value ), + ), + 'core/more' => array( + 'typography' => array( 'fontSize' => 'var(--undefined--font-size--small)' ), + 'color' => array( 'background' => 'linear-gradient(90deg, var(--wp--preset--color--primary) 0%, var(--wp--preset--color--secondary) 35%, var(--wp--undefined--color--secondary) 100%)' ), + ), + 'core/comment-content' => array( + 'typography' => array( 'fontSize' => 'calc(var(--wp--preset--font-size--small, 12px) + 20px)' ), + 'color' => array( + 'text' => 'var(--wp--preset--color--primary, red)', + 'background' => 'var(--wp--preset--color--primary, var(--wp--preset--font-size--secondary))', + 'link' => 'var(--undefined--color--primary, var(--wp--preset--font-size--secondary))', + ), + ), + 'core/comments' => array( + 'color' => array( + 'text' => 'var(--undefined--color--primary, var(--wp--preset--font-size--small))', + 'background' => 'var(--wp--preset--color--primary, var(--undefined--color--primary))', + ), + ), + 'core/navigation' => array( + 'elements' => array( + 'link' => array( + 'color' => array( + 'background' => 'var(--wp--preset--color--primary)', + 'text' => 'var(--wp--preset--color--secondary)', + ), + 'typography' => array( + 'fontSize' => 'var(--wp--preset--font-size--large)', + ), + ), + ), + ), + 'core/quote' => array( + 'typography' => array( 'fontSize' => 'var(--wp--preset--font-size--large)' ), + 'color' => array( 'background' => 'var(--wp--preset--color--primary)' ), + 'variations' => array( + 'plain' => array( + 'typography' => array( 'fontSize' => 'var(--wp--preset--font-size--small)' ), + 'color' => array( 'background' => 'var(--wp--preset--color--secondary)' ), + ), + ), + ), + ), + ), + ) + ); + + $styles = $theme_json::resolve_variables( $theme_json )->get_raw_data()['styles']; + + $this->assertEquals( $primary_color, $styles['color']['background'], 'Top level: Assert values are converted' ); + $this->assertEquals( $raw_color_value, $styles['color']['text'], 'Top level: Assert raw values stay intact' ); + + $this->assertEquals( $contrast_color, $styles['elements']['button']['color']['text'], 'Elements: color' ); + $this->assertEquals( $small_font, $styles['elements']['button']['typography']['fontSize'], 'Elements: font-size' ); + + $this->assertEquals( $large_font, $styles['blocks']['core/quote']['typography']['fontSize'], 'Blocks: font-size' ); + $this->assertEquals( $primary_color, $styles['blocks']['core/quote']['color']['background'], 'Blocks: color' ); + $this->assertEquals( $raw_color_value, $styles['blocks']['core/post-terms']['color']['background'], 'Blocks: Raw color value stays intact' ); + $this->assertEquals( $small_font, $styles['blocks']['core/post-terms']['typography']['fontSize'], 'Block core/post-terms: font-size' ); + $this->assertEquals( + "linear-gradient(90deg, $primary_color 0%, $secondary_color 35%, var(--wp--undefined--color--secondary) 100%)", + $styles['blocks']['core/more']['color']['background'], + 'Blocks: multiple colors and undefined color' + ); + $this->assertEquals( 'var(--undefined--font-size--small)', $styles['blocks']['core/more']['typography']['fontSize'], 'Blocks: undefined font-size ' ); + $this->assertEquals( "calc($small_font + 20px)", $styles['blocks']['core/comment-content']['typography']['fontSize'], 'Blocks: font-size in random place' ); + $this->assertEquals( $primary_color, $styles['blocks']['core/comment-content']['color']['text'], 'Blocks: text color with fallback' ); + $this->assertEquals( $primary_color, $styles['blocks']['core/comment-content']['color']['background'], 'Blocks: background color with var as fallback' ); + $this->assertEquals( $primary_color, $styles['blocks']['core/navigation']['elements']['link']['color']['background'], 'Block element: background color' ); + $this->assertEquals( $secondary_color, $styles['blocks']['core/navigation']['elements']['link']['color']['text'], 'Block element: text color' ); + $this->assertEquals( $large_font, $styles['blocks']['core/navigation']['elements']['link']['typography']['fontSize'], 'Block element: font-size' ); + + $this->assertEquals( + "var(--undefined--color--primary, $small_font)", + $styles['blocks']['core/comments']['color']['text'], + 'Blocks: text color with undefined var and fallback' + ); + $this->assertEquals( + $primary_color, + $styles['blocks']['core/comments']['color']['background'], + 'Blocks: background color with variable and undefined fallback' + ); + + $this->assertEquals( $small_font, $styles['blocks']['core/quote']['variations']['plain']['typography']['fontSize'], 'Block variations: font-size' ); + $this->assertEquals( $secondary_color, $styles['blocks']['core/quote']['variations']['plain']['color']['background'], 'Block variations: color' ); + } + } From c43d055e7e7f140da105b7b0879d9d92576374f7 Mon Sep 17 00:00:00 2001 From: Siobhan Bamber Date: Mon, 19 Jun 2023 11:42:08 +0100 Subject: [PATCH 092/163] [RNMobile] Ensure dictating text doesn't cause cursor to be offset on iOS (#51528) The changes in this PR ensure that the cursor's position is always correct during dictation on iOS. --- .../react-native-aztec/ios/RNTAztecView/RCTAztecView.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift b/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift index 3504da36af422a..d1680a051851fc 100644 --- a/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift +++ b/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift @@ -356,8 +356,10 @@ class RCTAztecView: Aztec.TextView { // Replace occurrences of the obj symbol ("\u{FFFC}") textView.text = textView.text?.replacingOccurrences(of: "\u{FFFC}", with: "") - - if let newPosition = textView.position(from: textView.beginningOfDocument, offset: originalPosition) { + + // Detect if cursor is off-by-one and correct, if so + let newPositionOffset = originalPosition > 0 ? originalPosition - 1 : originalPosition + if let newPosition = textView.position(from: textView.beginningOfDocument, offset: newPositionOffset) { // Move the cursor to the correct, new position following dictation textView.selectedTextRange = textView.textRange(from: newPosition, to: newPosition) } From 4b180ab2065a0e330cd7e0b84b1ed2bcaa9b9ebc Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 19 Jun 2023 20:57:51 +1000 Subject: [PATCH 093/163] Prevent BlockBreadcrumb from re-rendering unnecessarily when typing (#51628) --- .../src/components/block-breadcrumb/index.js | 8 +- .../src/store/private-selectors.js | 26 +++++ .../src/store/test/private-selectors.js | 96 +++++++++++++++++++ 3 files changed, 124 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-breadcrumb/index.js b/packages/block-editor/src/components/block-breadcrumb/index.js index 9e4ab5391d8c44..13cc93d41a981d 100644 --- a/packages/block-editor/src/components/block-breadcrumb/index.js +++ b/packages/block-editor/src/components/block-breadcrumb/index.js @@ -26,15 +26,11 @@ function BlockBreadcrumb( { rootLabelText } ) { const { getSelectionStart, getSelectedBlockClientId, - getBlockParents, - getBlockEditingMode, + getEnabledBlockParents, } = unlock( select( blockEditorStore ) ); const selectedBlockClientId = getSelectedBlockClientId(); return { - parents: getBlockParents( selectedBlockClientId ).filter( - ( parentClientId ) => - getBlockEditingMode( parentClientId ) !== 'disabled' - ), + parents: getEnabledBlockParents( selectedBlockClientId ), clientId: selectedBlockClientId, hasSelection: !! getSelectionStart().clientId, }; diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index c250b649569f0c..1eff2e8510dcc5 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -17,6 +17,7 @@ import { getTemplateLock, getBlockName, getBlockOrder, + getBlockParents, } from './selectors'; /** @@ -169,3 +170,28 @@ export const getListViewClientIdsTree = createSelector( state.blockListSettings, ] ); + +/** + * Returns a list of a given block's ancestors, from top to bottom. Blocks with + * a 'disabled' editing mode are excluded. + * + * @see getBlockParents + * + * @param {Object} state Global application state. + * @param {string} clientId The block client ID. + * @param {boolean} ascending Order results from bottom to top (true) or top + * to bottom (false). + */ +export const getEnabledBlockParents = createSelector( + ( state, clientId, ascending = false ) => { + return getBlockParents( state, clientId, ascending ).filter( + ( parent ) => getBlockEditingMode( state, parent ) !== 'disabled' + ); + }, + ( state ) => [ + state.blocks.parents, + state.blockEditingModes, + state.settings.templateLock, + state.blockListSettings, + ] +); diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index ab0d4b11c5bf40..30cf702c605263 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -12,6 +12,7 @@ import { getBlockEditingMode, isBlockSubtreeDisabled, getListViewClientIdsTree, + getEnabledBlockParents, } from '../private-selectors'; jest.mock( '@wordpress/data/src/select', () => ( { @@ -554,4 +555,99 @@ describe( 'private selectors', () => { ] ); } ); } ); + + describe( 'getEnabledBlockParents', () => { + it( 'should return an empty array if block is at the root', () => { + const state = { + settings: {}, + blocks: { + parents: new Map( [ + [ '6cf70164-9097-4460-bcbf-200560546988', '' ], + ] ), + }, + blockEditingModes: new Map(), + }; + expect( + getEnabledBlockParents( + state, + '6cf70164-9097-4460-bcbf-200560546988' + ) + ).toEqual( [] ); + } ); + + it( 'should return non-disabled parents', () => { + const state = { + settings: {}, + blocks: { + parents: new Map( [ + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', '' ], + [ + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + [ + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], + [ + '4c2b7140-fffd-44b4-b2a7-820c670a6514', + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + ], + ] ), + }, + blockEditingModes: new Map( [ + [ '', 'disabled' ], + [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', 'default' ], + ] ), + blockListSettings: {}, + }; + expect( + getEnabledBlockParents( + state, + '4c2b7140-fffd-44b4-b2a7-820c670a6514' + ) + ).toEqual( [ + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + ] ); + } ); + + it( 'should order from bottom to top if ascending is true', () => { + const state = { + settings: {}, + blocks: { + parents: new Map( [ + [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', '' ], + [ + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + ], + [ + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], + [ + '4c2b7140-fffd-44b4-b2a7-820c670a6514', + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + ], + ] ), + }, + blockEditingModes: new Map( [ + [ '', 'disabled' ], + [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', 'default' ], + ] ), + blockListSettings: {}, + }; + expect( + getEnabledBlockParents( + state, + '4c2b7140-fffd-44b4-b2a7-820c670a6514', + true + ) + ).toEqual( [ + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ] ); + } ); + } ); } ); From a40838d1634056b3b94b4fb345fa33c0b341cd6a Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 12 Jun 2023 15:57:47 +0300 Subject: [PATCH 094/163] HTML-API: Backport updates from Core This is a "blessed" PR, bringing back changes that were introduced to the HTML API in Trac/SVG that need to exist in Gutenberg to avoid introducing breakage and disharmony between the two repositories. cc: @sergeybiryukov --- .../html-api/class-wp-html-tag-processor.php | 378 ++++++++---------- ...class-gutenberg-html-tag-processor-6-3.php | 325 +++++++-------- 2 files changed, 311 insertions(+), 392 deletions(-) diff --git a/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php b/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php index f06302d4b742ff..7edb67f9f0423e 100644 --- a/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php +++ b/lib/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php @@ -38,12 +38,11 @@ * 3. Request changes to the attributes in those tag(s). * * Example: - * ```php - * $tags = new WP_HTML_Tag_Processor( $html ); - * if ( $tags->next_tag( 'option' ) ) { - * $tags->set_attribute( 'selected', true ); - * } - * ``` + * + * $tags = new WP_HTML_Tag_Processor( $html ); + * if ( $tags->next_tag( 'option' ) ) { + * $tags->set_attribute( 'selected', true ); + * } * * ### Finding tags * @@ -54,9 +53,8 @@ * regardless of what kind it is. * * If you want to _find whatever the next tag is_: - * ```php - * $tags->next_tag(); - * ``` + * + * $tags->next_tag(); * * | Goal | Query | * |-----------------------------------------------------------|---------------------------------------------------------------------------------| @@ -87,19 +85,18 @@ * provided by the processor or external state or variables. * * Example: - * ```php - * // Paint up to the first five DIV or SPAN tags marked with the "jazzy" style. - * $remaining_count = 5; - * while ( $remaining_count > 0 && $tags->next_tag() ) { - * if ( - * ( 'DIV' === $tags->get_tag() || 'SPAN' === $tags->get_tag() ) && - * 'jazzy' === $tags->get_attribute( 'data-style' ) - * ) { - * $tags->add_class( 'theme-style-everest-jazz' ); - * $remaining_count--; + * + * // Paint up to the first five DIV or SPAN tags marked with the "jazzy" style. + * $remaining_count = 5; + * while ( $remaining_count > 0 && $tags->next_tag() ) { + * if ( + * ( 'DIV' === $tags->get_tag() || 'SPAN' === $tags->get_tag() ) && + * 'jazzy' === $tags->get_attribute( 'data-style' ) + * ) { + * $tags->add_class( 'theme-style-everest-jazz' ); + * $remaining_count--; + * } * } - * } - * ``` * * `get_attribute()` will return `null` if the attribute wasn't present * on the tag when it was called. It may return `""` (the empty string) @@ -116,12 +113,11 @@ * nothing and move on to the next opening tag. * * Example: - * ```php - * if ( $tags->next_tag( array( 'class' => 'wp-group-block' ) ) ) { - * $tags->set_attribute( 'title', 'This groups the contained content.' ); - * $tags->remove_attribute( 'data-test-id' ); - * } - * ``` + * + * if ( $tags->next_tag( array( 'class' => 'wp-group-block' ) ) ) { + * $tags->set_attribute( 'title', 'This groups the contained content.' ); + * $tags->remove_attribute( 'data-test-id' ); + * } * * If `set_attribute()` is called for an existing attribute it will * overwrite the existing value. Similarly, calling `remove_attribute()` @@ -141,31 +137,30 @@ * entire `class` attribute will be removed. * * Example: - * ```php - * // from `Yippee!` - * // to `Yippee!` - * $tags->add_class( 'is-active' ); * - * // from `Yippee!` - * // to `Yippee!` - * $tags->add_class( 'is-active' ); + * // from `Yippee!` + * // to `Yippee!` + * $tags->add_class( 'is-active' ); + * + * // from `Yippee!` + * // to `Yippee!` + * $tags->add_class( 'is-active' ); * - * // from `Yippee!` - * // to `Yippee!` - * $tags->add_class( 'is-active' ); + * // from `Yippee!` + * // to `Yippee!` + * $tags->add_class( 'is-active' ); * - * // from `` - * // to ` - * $tags->remove_class( 'rugby' ); + * // from `` + * // to ` + * $tags->remove_class( 'rugby' ); * - * // from `` - * // to ` - * $tags->remove_class( 'rugby' ); + * // from `` + * // to ` + * $tags->remove_class( 'rugby' ); * - * // from `` - * // to ` - * $tags->remove_class( 'rugby' ); - * ``` + * // from `` + * // to ` + * $tags->remove_class( 'rugby' ); * * When class changes are enqueued but a direct change to `class` is made via * `set_attribute` then the changes to `set_attribute` (or `remove_attribute`) @@ -184,26 +179,24 @@ * and so on. It's fine from a performance standpoint to create a * bookmark and update it frequently, such as within a loop. * - * ```php - * $total_todos = 0; - * while ( $p->next_tag( array( 'tag_name' => 'UL', 'class_name' => 'todo' ) ) ) { - * $p->set_bookmark( 'list-start' ); - * while ( $p->next_tag( array( 'tag_closers' => 'visit' ) ) ) { - * if ( 'UL' === $p->get_tag() && $p->is_tag_closer() ) { - * $p->set_bookmark( 'list-end' ); - * $p->seek( 'list-start' ); - * $p->set_attribute( 'data-contained-todos', (string) $total_todos ); - * $total_todos = 0; - * $p->seek( 'list-end' ); - * break; - * } + * $total_todos = 0; + * while ( $p->next_tag( array( 'tag_name' => 'UL', 'class_name' => 'todo' ) ) ) { + * $p->set_bookmark( 'list-start' ); + * while ( $p->next_tag( array( 'tag_closers' => 'visit' ) ) ) { + * if ( 'UL' === $p->get_tag() && $p->is_tag_closer() ) { + * $p->set_bookmark( 'list-end' ); + * $p->seek( 'list-start' ); + * $p->set_attribute( 'data-contained-todos', (string) $total_todos ); + * $total_todos = 0; + * $p->seek( 'list-end' ); + * break; + * } * - * if ( 'LI' === $p->get_tag() && ! $p->is_tag_closer() ) { - * $total_todos++; + * if ( 'LI' === $p->get_tag() && ! $p->is_tag_closer() ) { + * $total_todos++; + * } * } * } - * } - * ``` * * ## Design and limitations * @@ -229,11 +222,11 @@ * The Tag Processor's design incorporates a "garbage-in-garbage-out" philosophy. * HTML5 specifies that certain invalid content be transformed into different forms * for display, such as removing null bytes from an input document and replacing - * invalid characters with the Unicode replacement character `U+FFFD` (visually "�"). - * Where errors or transformations exist within the HTML5 specification, the Tag Processor - * leaves those invalid inputs untouched, passing them through to the final browser - * to handle. While this implies that certain operations will be non-spec-compliant, - * such as reading the value of an attribute with invalid content, it also preserves a + * invalid characters with the Unicode replacement character U+FFFD �. Where errors + * or transformations exist within the HTML5 specification, the Tag Processor leaves + * those invalid inputs untouched, passing them through to the final browser to handle. + * While this implies that certain operations will be non-spec-compliant, such as + * reading the value of an attribute with invalid content, it also preserves a * simplicity and efficiency for handling those error cases. * * Most operations within the Tag Processor are designed to minimize the difference @@ -335,11 +328,10 @@ class WP_HTML_Tag_Processor { * Byte offset in input document where current tag name starts. * * Example: - * ``` - *
    ... - * 01234 - * - tag name starts at 1 - * ``` + * + *
    ... + * 01234 + * - tag name starts at 1 * * @since 6.2.0 * @var int|null @@ -350,11 +342,10 @@ class WP_HTML_Tag_Processor { * Byte length of current tag name. * * Example: - * ``` - *
    ... - * 01234 - * --- tag name length is 3 - * ``` + * + *
    ... + * 01234 + * --- tag name length is 3 * * @since 6.2.0 * @var int|null @@ -365,12 +356,11 @@ class WP_HTML_Tag_Processor { * Byte offset in input document where current tag token ends. * * Example: - * ``` - *
    ... - * 0 1 | - * 01234567890123456 - * --- tag name ends at 14 - * ``` + * + *
    ... + * 0 1 | + * 01234567890123456 + * --- tag name ends at 14 * * @since 6.2.0 * @var int|null @@ -388,25 +378,24 @@ class WP_HTML_Tag_Processor { * Lazily-built index of attributes found within an HTML tag, keyed by the attribute name. * * Example: - * ```php - * // supposing the parser is working through this content - * // and stops after recognizing the `id` attribute - * //
    - * // ^ parsing will continue from this point - * $this->attributes = array( - * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ) - * ); - * - * // when picking up parsing again, or when asking to find the - * // `class` attribute we will continue and add to this array - * $this->attributes = array( - * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ), - * 'class' => new WP_HTML_Attribute_Match( 'class', 'outline', 18, 32 ) - * ); - * - * // Note that only the `class` attribute value is stored in the index. - * // That's because it is the only value used by this class at the moment. - * ``` + * + * // Supposing the parser is working through this content + * // and stops after recognizing the `id` attribute. + * //
    + * // ^ parsing will continue from this point. + * $this->attributes = array( + * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ) + * ); + * + * // When picking up parsing again, or when asking to find the + * // `class` attribute we will continue and add to this array. + * $this->attributes = array( + * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ), + * 'class' => new WP_HTML_Attribute_Match( 'class', 'outline', 18, 32 ) + * ); + * + * // Note that only the `class` attribute value is stored in the index. + * // That's because it is the only value used by this class at the moment. * * @since 6.2.0 * @var WP_HTML_Attribute_Token[] @@ -425,14 +414,13 @@ class WP_HTML_Tag_Processor { * into a single `set_attribute( 'class', $changes )` call. * * Example: - * ```php - * // Add the `wp-block-group` class, remove the `wp-group` class. - * $classname_updates = array( - * // Indexed by a comparable class name - * 'wp-block-group' => WP_HTML_Tag_Processor::ADD_CLASS, - * 'wp-group' => WP_HTML_Tag_Processor::REMOVE_CLASS - * ); - * ``` + * + * // Add the `wp-block-group` class, remove the `wp-group` class. + * $classname_updates = array( + * // Indexed by a comparable class name. + * 'wp-block-group' => WP_HTML_Tag_Processor::ADD_CLASS, + * 'wp-group' => WP_HTML_Tag_Processor::REMOVE_CLASS + * ); * * @since 6.2.0 * @var bool[] @@ -479,18 +467,17 @@ class WP_HTML_Tag_Processor { * copies when applying many updates to a single document. * * Example: - * ```php - * // Replace an attribute stored with a new value, indices - * // sourced from the lazily-parsed HTML recognizer. - * $start = $attributes['src']->start; - * $end = $attributes['src']->end; - * $modifications[] = new WP_HTML_Text_Replacement( $start, $end, $new_value ); - * - * // Correspondingly, something like this will appear in this array. - * $lexical_updates = array( - * WP_HTML_Text_Replacement( 14, 28, 'https://my-site.my-domain/wp-content/uploads/2014/08/kittens.jpg' ) - * ); - * ``` + * + * // Replace an attribute stored with a new value, indices + * // sourced from the lazily-parsed HTML recognizer. + * $start = $attributes['src']->start; + * $end = $attributes['src']->end; + * $modifications[] = new WP_HTML_Text_Replacement( $start, $end, $new_value ); + * + * // Correspondingly, something like this will appear in this array. + * $lexical_updates = array( + * WP_HTML_Text_Replacement( 14, 28, 'https://my-site.my-domain/wp-content/uploads/2014/08/kittens.jpg' ) + * ); * * @since 6.2.0 * @var WP_HTML_Text_Replacement[] @@ -608,7 +595,7 @@ public function next_tag( $query = null ) { * Release bookmarks when they are no longer needed. * * Example: - * ``` + * *

    Surprising fact you may not know!

    * ^ ^ * \-|-- this `H2` opener bookmark tracks the token @@ -616,14 +603,13 @@ public function next_tag( $query = null ) { *

    Surprising fact you may no… * ^ ^ * \-|-- it shifts with edits - * ``` * * Bookmarks provide the ability to seek to a previously-scanned * place in the HTML document. This avoids the need to re-scan * the entire document. * * Example: - * ``` + * *
    • One
    • Two
    • Three
    * ^^^^ * want to note this last item @@ -650,7 +636,6 @@ public function next_tag( $query = null ) { * $p->set_bookmark( 'last-li' ); * } * } - * ``` * * Bookmarks intentionally hide the internal string offsets * to which they refer. They are maintained internally as @@ -727,7 +712,7 @@ public function release_bookmark( $name ) { * * @see https://html.spec.whatwg.org/multipage/parsing.html#rcdata-state * - * @param string $tag_name – the lowercase tag name which will close the RCDATA region. + * @param string $tag_name The lowercase tag name which will close the RCDATA region. * @return bool Whether an end to the RCDATA region was found before the end of the document. */ private function skip_rcdata( $tag_name ) { @@ -1430,7 +1415,6 @@ private function class_name_updates_to_attributes_updates() { * * @since 6.2.0 * @since 6.2.1 Accumulates shift for internal cursor and passed pointer. - * @since 6.3.0 Invalidate any bookmarks whose targets are overwritten. * * @param int $shift_this_point Accumulate and return shift for this position. * @return int How many bytes the given pointer moved in response to the updates. @@ -1480,7 +1464,7 @@ private function apply_attributes_updates( $shift_this_point = 0 ) { * Adjust bookmark locations to account for how the text * replacements adjust offsets in the input document. */ - foreach ( $this->bookmarks as $bookmark_name => $bookmark ) { + foreach ( $this->bookmarks as $bookmark ) { /* * Each lexical update which appears before the bookmark's endpoints * might shift the offsets for those endpoints. Loop through each change @@ -1491,22 +1475,20 @@ private function apply_attributes_updates( $shift_this_point = 0 ) { $tail_delta = 0; foreach ( $this->lexical_updates as $diff ) { - if ( $bookmark->start < $diff->start && $bookmark->end < $diff->start ) { - break; - } + $update_head = $bookmark->start >= $diff->start; + $update_tail = $bookmark->end >= $diff->start; - if ( $bookmark->start >= $diff->start && $bookmark->end < $diff->end ) { - $this->release_bookmark( $bookmark_name ); - continue 2; + if ( ! $update_head && ! $update_tail ) { + break; } $delta = strlen( $diff->text ) - ( $diff->end - $diff->start ); - if ( $bookmark->start >= $diff->start ) { + if ( $update_head ) { $head_delta += $delta; } - if ( $bookmark->end >= $diff->end ) { + if ( $update_tail ) { $tail_delta += $delta; } } @@ -1520,18 +1502,6 @@ private function apply_attributes_updates( $shift_this_point = 0 ) { return $accumulated_shift_for_given_point; } - /** - * Checks whether a bookmark with the given name exists. - * - * @since 6.3.0 - * - * @param string $bookmark_name Name to identify a bookmark that potentially exists. - * @return bool Whether that bookmark exists. - */ - public function has_bookmark( $bookmark_name ) { - return array_key_exists( $bookmark_name, $this->bookmarks ); - } - /** * Move the internal cursor in the Tag Processor to a given bookmark's location. * @@ -1630,10 +1600,9 @@ private function get_enqueued_attribute_value( $comparable_name ) { * or trailing whitespace, and that the casing follows the name given in `set_attribute`. * * Example: - * ``` + * * $p->set_attribute( 'data-TEST-id', 'update' ); * 'update' === $p->get_enqueued_attribute_value( 'data-test-id' ); - * ``` * * Detect this difference based on the absence of the `=`, which _must_ exist in any * attribute containing a value, e.g. ``. @@ -1664,16 +1633,15 @@ private function get_enqueued_attribute_value( $comparable_name ) { * Returns the value of a requested attribute from a matched tag opener if that attribute exists. * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); - * $p->next_tag( array( 'class_name' => 'test' ) ) === true; - * $p->get_attribute( 'data-test-id' ) === '14'; - * $p->get_attribute( 'enabled' ) === true; - * $p->get_attribute( 'aria-label' ) === null; * - * $p->next_tag() === false; - * $p->get_attribute( 'class' ) === null; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); + * $p->next_tag( array( 'class_name' => 'test' ) ) === true; + * $p->get_attribute( 'data-test-id' ) === '14'; + * $p->get_attribute( 'enabled' ) === true; + * $p->get_attribute( 'aria-label' ) === null; + * + * $p->next_tag() === false; + * $p->get_attribute( 'class' ) === null; * * @since 6.2.0 * @@ -1745,14 +1713,13 @@ public function get_attribute( $name ) { * - HTML 5 spec * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); - * $p->next_tag( array( 'class_name' => 'test' ) ) === true; - * $p->get_attribute_names_with_prefix( 'data-' ) === array( 'data-enabled', 'data-test-id' ); * - * $p->next_tag() === false; - * $p->get_attribute_names_with_prefix( 'data-' ) === null; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); + * $p->next_tag( array( 'class_name' => 'test' ) ) === true; + * $p->get_attribute_names_with_prefix( 'data-' ) === array( 'data-enabled', 'data-test-id' ); + * + * $p->next_tag() === false; + * $p->get_attribute_names_with_prefix( 'data-' ) === null; * * @since 6.2.0 * @@ -1781,14 +1748,13 @@ function get_attribute_names_with_prefix( $prefix ) { * Returns the uppercase name of the matched tag. * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); - * $p->next_tag() === true; - * $p->get_tag() === 'DIV'; * - * $p->next_tag() === false; - * $p->get_tag() === null; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); + * $p->next_tag() === true; + * $p->get_tag() === 'DIV'; + * + * $p->next_tag() === false; + * $p->get_tag() === null; * * @since 6.2.0 * @@ -1804,43 +1770,17 @@ public function get_tag() { return strtoupper( $tag_name ); } - /** - * Indicates if the currently matched tag contains the self-closing flag. - * - * No HTML elements ought to have the self-closing flag and for those, the self-closing - * flag will be ignored. For void elements this is benign because they "self close" - * automatically. For non-void HTML elements though problems will appear if someone - * intends to use a self-closing element in place of that element with an empty body. - * For HTML foreign elements and custom elements the self-closing flag determines if - * they self-close or not. - * - * This function does not determine if a tag is self-closing, - * but only if the self-closing flag is present in the syntax. - * - * @since 6.3.0 - * - * @return bool Whether the currently matched tag contains the self-closing flag. - */ - public function has_self_closing_flag() { - if ( ! $this->tag_name_starts_at ) { - return false; - } - - return '/' === $this->html[ $this->tag_ends_at - 1 ]; - } - /** * Indicates if the current tag token is a tag closer. * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    ' ); - * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); - * $p->is_tag_closer() === false; * - * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); - * $p->is_tag_closer() === true; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    ' ); + * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); + * $p->is_tag_closer() === false; + * + * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); + * $p->is_tag_closer() === true; * * @since 6.2.0 * @@ -1946,12 +1886,13 @@ public function set_attribute( $name, $value ) { * Update an existing attribute. * * Example – set attribute id to "new" in
    : - *
    - * ^-------------^ - * start end - * replacement: `id="new"` * - * Result:
    + *
    + * ^-------------^ + * start end + * replacement: `id="new"` + * + * Result:
    */ $existing_attribute = $this->attributes[ $comparable_name ]; $this->lexical_updates[ $comparable_name ] = new WP_HTML_Text_Replacement( @@ -1964,12 +1905,13 @@ public function set_attribute( $name, $value ) { * Create a new attribute at the tag's name end. * * Example – add attribute id="new" to
    : - *
    - * ^ - * start and end - * replacement: ` id="new"` * - * Result:
    + *
    + * ^ + * start and end + * replacement: ` id="new"` + * + * Result:
    */ $this->lexical_updates[ $comparable_name ] = new WP_HTML_Text_Replacement( $this->tag_name_starts_at + $this->tag_name_length, diff --git a/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php b/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php index 5b38484f659acc..b869e2ef1e1c16 100644 --- a/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php +++ b/lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php @@ -1,5 +1,7 @@ next_tag( 'option' ) ) { - * $tags->set_attribute( 'selected', true ); - * } - * ``` + * + * $tags = new WP_HTML_Tag_Processor( $html ); + * if ( $tags->next_tag( 'option' ) ) { + * $tags->set_attribute( 'selected', true ); + * } * * ### Finding tags * @@ -54,9 +55,8 @@ * regardless of what kind it is. * * If you want to _find whatever the next tag is_: - * ```php - * $tags->next_tag(); - * ``` + * + * $tags->next_tag(); * * | Goal | Query | * |-----------------------------------------------------------|---------------------------------------------------------------------------------| @@ -87,19 +87,18 @@ * provided by the processor or external state or variables. * * Example: - * ```php - * // Paint up to the first five DIV or SPAN tags marked with the "jazzy" style. - * $remaining_count = 5; - * while ( $remaining_count > 0 && $tags->next_tag() ) { - * if ( - * ( 'DIV' === $tags->get_tag() || 'SPAN' === $tags->get_tag() ) && - * 'jazzy' === $tags->get_attribute( 'data-style' ) - * ) { - * $tags->add_class( 'theme-style-everest-jazz' ); - * $remaining_count--; + * + * // Paint up to the first five DIV or SPAN tags marked with the "jazzy" style. + * $remaining_count = 5; + * while ( $remaining_count > 0 && $tags->next_tag() ) { + * if ( + * ( 'DIV' === $tags->get_tag() || 'SPAN' === $tags->get_tag() ) && + * 'jazzy' === $tags->get_attribute( 'data-style' ) + * ) { + * $tags->add_class( 'theme-style-everest-jazz' ); + * $remaining_count--; + * } * } - * } - * ``` * * `get_attribute()` will return `null` if the attribute wasn't present * on the tag when it was called. It may return `""` (the empty string) @@ -116,12 +115,11 @@ * nothing and move on to the next opening tag. * * Example: - * ```php - * if ( $tags->next_tag( array( 'class' => 'wp-group-block' ) ) ) { - * $tags->set_attribute( 'title', 'This groups the contained content.' ); - * $tags->remove_attribute( 'data-test-id' ); - * } - * ``` + * + * if ( $tags->next_tag( array( 'class' => 'wp-group-block' ) ) ) { + * $tags->set_attribute( 'title', 'This groups the contained content.' ); + * $tags->remove_attribute( 'data-test-id' ); + * } * * If `set_attribute()` is called for an existing attribute it will * overwrite the existing value. Similarly, calling `remove_attribute()` @@ -141,31 +139,30 @@ * entire `class` attribute will be removed. * * Example: - * ```php - * // from `Yippee!` - * // to `Yippee!` - * $tags->add_class( 'is-active' ); * - * // from `Yippee!` - * // to `Yippee!` - * $tags->add_class( 'is-active' ); + * // from `Yippee!` + * // to `Yippee!` + * $tags->add_class( 'is-active' ); * - * // from `Yippee!` - * // to `Yippee!` - * $tags->add_class( 'is-active' ); + * // from `Yippee!` + * // to `Yippee!` + * $tags->add_class( 'is-active' ); * - * // from `` - * // to ` - * $tags->remove_class( 'rugby' ); + * // from `Yippee!` + * // to `Yippee!` + * $tags->add_class( 'is-active' ); * - * // from `` - * // to ` - * $tags->remove_class( 'rugby' ); + * // from `` + * // to ` + * $tags->remove_class( 'rugby' ); * - * // from `` - * // to ` - * $tags->remove_class( 'rugby' ); - * ``` + * // from `` + * // to ` + * $tags->remove_class( 'rugby' ); + * + * // from `` + * // to ` + * $tags->remove_class( 'rugby' ); * * When class changes are enqueued but a direct change to `class` is made via * `set_attribute` then the changes to `set_attribute` (or `remove_attribute`) @@ -184,26 +181,24 @@ * and so on. It's fine from a performance standpoint to create a * bookmark and update it frequently, such as within a loop. * - * ```php - * $total_todos = 0; - * while ( $p->next_tag( array( 'tag_name' => 'UL', 'class_name' => 'todo' ) ) ) { - * $p->set_bookmark( 'list-start' ); - * while ( $p->next_tag( array( 'tag_closers' => 'visit' ) ) ) { - * if ( 'UL' === $p->get_tag() && $p->is_tag_closer() ) { - * $p->set_bookmark( 'list-end' ); - * $p->seek( 'list-start' ); - * $p->set_attribute( 'data-contained-todos', (string) $total_todos ); - * $total_todos = 0; - * $p->seek( 'list-end' ); - * break; - * } + * $total_todos = 0; + * while ( $p->next_tag( array( 'tag_name' => 'UL', 'class_name' => 'todo' ) ) ) { + * $p->set_bookmark( 'list-start' ); + * while ( $p->next_tag( array( 'tag_closers' => 'visit' ) ) ) { + * if ( 'UL' === $p->get_tag() && $p->is_tag_closer() ) { + * $p->set_bookmark( 'list-end' ); + * $p->seek( 'list-start' ); + * $p->set_attribute( 'data-contained-todos', (string) $total_todos ); + * $total_todos = 0; + * $p->seek( 'list-end' ); + * break; + * } * - * if ( 'LI' === $p->get_tag() && ! $p->is_tag_closer() ) { - * $total_todos++; + * if ( 'LI' === $p->get_tag() && ! $p->is_tag_closer() ) { + * $total_todos++; + * } * } * } - * } - * ``` * * ## Design and limitations * @@ -335,11 +330,10 @@ class Gutenberg_HTML_Tag_Processor_6_3 { * Byte offset in input document where current tag name starts. * * Example: - * ``` - *
    ... - * 01234 - * - tag name starts at 1 - * ``` + * + *
    ... + * 01234 + * - tag name starts at 1 * * @since 6.2.0 * @var int|null @@ -350,11 +344,10 @@ class Gutenberg_HTML_Tag_Processor_6_3 { * Byte length of current tag name. * * Example: - * ``` - *
    ... - * 01234 - * --- tag name length is 3 - * ``` + * + *
    ... + * 01234 + * --- tag name length is 3 * * @since 6.2.0 * @var int|null @@ -365,12 +358,11 @@ class Gutenberg_HTML_Tag_Processor_6_3 { * Byte offset in input document where current tag token ends. * * Example: - * ``` - *
    ... - * 0 1 | - * 01234567890123456 - * --- tag name ends at 14 - * ``` + * + *
    ... + * 0 1 | + * 01234567890123456 + * --- tag name ends at 14 * * @since 6.2.0 * @var int|null @@ -388,25 +380,24 @@ class Gutenberg_HTML_Tag_Processor_6_3 { * Lazily-built index of attributes found within an HTML tag, keyed by the attribute name. * * Example: - * ```php - * // supposing the parser is working through this content - * // and stops after recognizing the `id` attribute - * //
    - * // ^ parsing will continue from this point - * $this->attributes = array( - * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ) - * ); - * - * // when picking up parsing again, or when asking to find the - * // `class` attribute we will continue and add to this array - * $this->attributes = array( - * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ), - * 'class' => new WP_HTML_Attribute_Match( 'class', 'outline', 18, 32 ) - * ); - * - * // Note that only the `class` attribute value is stored in the index. - * // That's because it is the only value used by this class at the moment. - * ``` + * + * // Supposing the parser is working through this content + * // and stops after recognizing the `id` attribute. + * //
    + * // ^ parsing will continue from this point. + * $this->attributes = array( + * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ) + * ); + * + * // When picking up parsing again, or when asking to find the + * // `class` attribute we will continue and add to this array. + * $this->attributes = array( + * 'id' => new WP_HTML_Attribute_Match( 'id', null, 6, 17 ), + * 'class' => new WP_HTML_Attribute_Match( 'class', 'outline', 18, 32 ) + * ); + * + * // Note that only the `class` attribute value is stored in the index. + * // That's because it is the only value used by this class at the moment. * * @since 6.2.0 * @var WP_HTML_Attribute_Token[] @@ -425,14 +416,13 @@ class Gutenberg_HTML_Tag_Processor_6_3 { * into a single `set_attribute( 'class', $changes )` call. * * Example: - * ```php - * // Add the `wp-block-group` class, remove the `wp-group` class. - * $classname_updates = array( - * // Indexed by a comparable class name - * 'wp-block-group' => WP_HTML_Tag_Processor::ADD_CLASS, - * 'wp-group' => WP_HTML_Tag_Processor::REMOVE_CLASS - * ); - * ``` + * + * // Add the `wp-block-group` class, remove the `wp-group` class. + * $classname_updates = array( + * // Indexed by a comparable class name. + * 'wp-block-group' => WP_HTML_Tag_Processor::ADD_CLASS, + * 'wp-group' => WP_HTML_Tag_Processor::REMOVE_CLASS + * ); * * @since 6.2.0 * @var bool[] @@ -479,18 +469,17 @@ class Gutenberg_HTML_Tag_Processor_6_3 { * copies when applying many updates to a single document. * * Example: - * ```php - * // Replace an attribute stored with a new value, indices - * // sourced from the lazily-parsed HTML recognizer. - * $start = $attributes['src']->start; - * $end = $attributes['src']->end; - * $modifications[] = new WP_HTML_Text_Replacement( $start, $end, $new_value ); - * - * // Correspondingly, something like this will appear in this array. - * $lexical_updates = array( - * WP_HTML_Text_Replacement( 14, 28, 'https://my-site.my-domain/wp-content/uploads/2014/08/kittens.jpg' ) - * ); - * ``` + * + * // Replace an attribute stored with a new value, indices + * // sourced from the lazily-parsed HTML recognizer. + * $start = $attributes['src']->start; + * $end = $attributes['src']->end; + * $modifications[] = new WP_HTML_Text_Replacement( $start, $end, $new_value ); + * + * // Correspondingly, something like this will appear in this array. + * $lexical_updates = array( + * WP_HTML_Text_Replacement( 14, 28, 'https://my-site.my-domain/wp-content/uploads/2014/08/kittens.jpg' ) + * ); * * @since 6.2.0 * @var WP_HTML_Text_Replacement[] @@ -608,7 +597,7 @@ public function next_tag( $query = null ) { * Release bookmarks when they are no longer needed. * * Example: - * ``` + * *

    Surprising fact you may not know!

    * ^ ^ * \-|-- this `H2` opener bookmark tracks the token @@ -616,14 +605,13 @@ public function next_tag( $query = null ) { *

    Surprising fact you may no… * ^ ^ * \-|-- it shifts with edits - * ``` * * Bookmarks provide the ability to seek to a previously-scanned * place in the HTML document. This avoids the need to re-scan * the entire document. * * Example: - * ``` + * *
    • One
    • Two
    • Three
    * ^^^^ * want to note this last item @@ -650,7 +638,6 @@ public function next_tag( $query = null ) { * $p->set_bookmark( 'last-li' ); * } * } - * ``` * * Bookmarks intentionally hide the internal string offsets * to which they refer. They are maintained internally as @@ -727,7 +714,7 @@ public function release_bookmark( $name ) { * * @see https://html.spec.whatwg.org/multipage/parsing.html#rcdata-state * - * @param string $tag_name – the lowercase tag name which will close the RCDATA region. + * @param string $tag_name The lowercase tag name which will close the RCDATA region. * @return bool Whether an end to the RCDATA region was found before the end of the document. */ private function skip_rcdata( $tag_name ) { @@ -1258,8 +1245,6 @@ private function parse_next_attribute() { * Move the internal cursor past any immediate successive whitespace. * * @since 6.2.0 - * - * @return void */ private function skip_whitespace() { $this->bytes_already_parsed += strspn( $this->html, " \t\f\r\n", $this->bytes_already_parsed ); @@ -1269,8 +1254,6 @@ private function skip_whitespace() { * Applies attribute updates and cleans up once a tag is fully parsed. * * @since 6.2.0 - * - * @return void */ private function after_tag() { $this->get_updated_html(); @@ -1289,8 +1272,6 @@ private function after_tag() { * * @see WP_HTML_Tag_Processor::$lexical_updates * @see WP_HTML_Tag_Processor::$classname_updates - * - * @return void */ private function class_name_updates_to_attributes_updates() { if ( count( $this->classname_updates ) === 0 ) { @@ -1630,10 +1611,9 @@ private function get_enqueued_attribute_value( $comparable_name ) { * or trailing whitespace, and that the casing follows the name given in `set_attribute`. * * Example: - * ``` + * * $p->set_attribute( 'data-TEST-id', 'update' ); * 'update' === $p->get_enqueued_attribute_value( 'data-test-id' ); - * ``` * * Detect this difference based on the absence of the `=`, which _must_ exist in any * attribute containing a value, e.g. ``. @@ -1664,16 +1644,15 @@ private function get_enqueued_attribute_value( $comparable_name ) { * Returns the value of a requested attribute from a matched tag opener if that attribute exists. * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); - * $p->next_tag( array( 'class_name' => 'test' ) ) === true; - * $p->get_attribute( 'data-test-id' ) === '14'; - * $p->get_attribute( 'enabled' ) === true; - * $p->get_attribute( 'aria-label' ) === null; * - * $p->next_tag() === false; - * $p->get_attribute( 'class' ) === null; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); + * $p->next_tag( array( 'class_name' => 'test' ) ) === true; + * $p->get_attribute( 'data-test-id' ) === '14'; + * $p->get_attribute( 'enabled' ) === true; + * $p->get_attribute( 'aria-label' ) === null; + * + * $p->next_tag() === false; + * $p->get_attribute( 'class' ) === null; * * @since 6.2.0 * @@ -1745,14 +1724,13 @@ public function get_attribute( $name ) { * - HTML 5 spec * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); - * $p->next_tag( array( 'class_name' => 'test' ) ) === true; - * $p->get_attribute_names_with_prefix( 'data-' ) === array( 'data-enabled', 'data-test-id' ); * - * $p->next_tag() === false; - * $p->get_attribute_names_with_prefix( 'data-' ) === null; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); + * $p->next_tag( array( 'class_name' => 'test' ) ) === true; + * $p->get_attribute_names_with_prefix( 'data-' ) === array( 'data-enabled', 'data-test-id' ); + * + * $p->next_tag() === false; + * $p->get_attribute_names_with_prefix( 'data-' ) === null; * * @since 6.2.0 * @@ -1781,14 +1759,13 @@ function get_attribute_names_with_prefix( $prefix ) { * Returns the uppercase name of the matched tag. * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); - * $p->next_tag() === true; - * $p->get_tag() === 'DIV'; * - * $p->next_tag() === false; - * $p->get_tag() === null; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    Test
    ' ); + * $p->next_tag() === true; + * $p->get_tag() === 'DIV'; + * + * $p->next_tag() === false; + * $p->get_tag() === null; * * @since 6.2.0 * @@ -1833,14 +1810,13 @@ public function has_self_closing_flag() { * Indicates if the current tag token is a tag closer. * * Example: - * ```php - * $p = new WP_HTML_Tag_Processor( '
    ' ); - * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); - * $p->is_tag_closer() === false; * - * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); - * $p->is_tag_closer() === true; - * ``` + * $p = new WP_HTML_Tag_Processor( '
    ' ); + * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); + * $p->is_tag_closer() === false; + * + * $p->next_tag( array( 'tag_name' => 'div', 'tag_closers' => 'visit' ) ); + * $p->is_tag_closer() === true; * * @since 6.2.0 * @@ -1946,12 +1922,13 @@ public function set_attribute( $name, $value ) { * Update an existing attribute. * * Example – set attribute id to "new" in
    : - *
    - * ^-------------^ - * start end - * replacement: `id="new"` * - * Result:
    + *
    + * ^-------------^ + * start end + * replacement: `id="new"` + * + * Result:
    */ $existing_attribute = $this->attributes[ $comparable_name ]; $this->lexical_updates[ $comparable_name ] = new WP_HTML_Text_Replacement( @@ -1964,12 +1941,13 @@ public function set_attribute( $name, $value ) { * Create a new attribute at the tag's name end. * * Example – add attribute id="new" to
    : - *
    - * ^ - * start and end - * replacement: ` id="new"` * - * Result:
    + *
    + * ^ + * start and end + * replacement: ` id="new"` + * + * Result:
    */ $this->lexical_updates[ $comparable_name ] = new WP_HTML_Text_Replacement( $this->tag_name_starts_at + $this->tag_name_length, @@ -2183,7 +2161,6 @@ public function get_updated_html() { * @type string|null $class_name Tag must contain this class name to match. * @type string $tag_closers "visit" or "skip": whether to stop on tag closers, e.g.
    . * } - * @return void */ private function parse_query( $query ) { if ( null !== $query && $query === $this->last_query ) { From cee85aacdb6e1744fc7f9766bd6c51bf944bd876 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Mon, 19 Jun 2023 15:05:48 +0300 Subject: [PATCH 095/163] Fix template display in page details with a custom template (#51638) * Fix template display in page details with a custom template * address feedback --- .../page-details.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js index 996e1c93773144..290ae4907f9a9e 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js @@ -95,15 +95,15 @@ function getPageDetails( page ) { export default function PageDetails( { id } ) { const { record } = useEntityRecord( 'postType', 'page', id ); - const { parentTitle, templateTitle } = useSelect( ( select ) => { - const { getEditedPostContext, getSettings } = unlock( - select( editSiteStore ) - ); - const defaultTemplateTypes = getSettings()?.defaultTemplateTypes; + const { getEditedPostContext } = unlock( select( editSiteStore ) ); const postContext = getEditedPostContext(); - + const templates = select( coreStore ).getEntityRecords( + 'postType', + 'wp_template', + { per_page: -1 } + ); // Template title. const templateSlug = // Checks that the post type matches the current theme's post type, otherwise @@ -112,10 +112,10 @@ export default function PageDetails( { id } ) { ? postContext?.templateSlug : null; const _templateTitle = - defaultTemplateTypes && templateSlug - ? defaultTemplateTypes.find( + templates && templateSlug + ? templates.find( ( template ) => template.slug === templateSlug - )?.title + )?.title?.rendered : null; // Parent page title. @@ -135,7 +135,7 @@ export default function PageDetails( { id } ) { templateTitle: _templateTitle, }; }, - [ record ] + [ record?.parent ] ); return ( From ce4dd6167517ed52de1a120ee4015942a7f73513 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Mon, 19 Jun 2023 15:06:08 +0300 Subject: [PATCH 096/163] [Query Loop]: Properly initialize and update `perPage` when we inherit from global query (#51641) --- .../src/query/edit/query-content.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/query/edit/query-content.js b/packages/block-library/src/query/edit/query-content.js index 567199f38e1b34..1d795dd646d48d 100644 --- a/packages/block-library/src/query/edit/query-content.js +++ b/packages/block-library/src/query/edit/query-content.js @@ -13,6 +13,7 @@ import { } from '@wordpress/block-editor'; import { SelectControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -35,6 +36,7 @@ export default function QueryContent( { query, displayLayout, tagName: TagName = 'div', + query: { inherit } = {}, } = attributes; const { __unstableMarkNextChangeAsNotPersistent } = useDispatch( blockEditorStore ); @@ -45,9 +47,12 @@ export default function QueryContent( { } ); const { postsPerPage } = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); + const { getEntityRecord, canUser } = select( coreStore ); + const settingPerPage = canUser( 'read', 'settings' ) + ? +getEntityRecord( 'root', 'site' )?.posts_per_page + : +getSettings().postsPerPage; return { - postsPerPage: - +getSettings().postsPerPage || DEFAULTS_POSTS_PER_PAGE, + postsPerPage: settingPerPage || DEFAULTS_POSTS_PER_PAGE, }; }, [] ); // There are some effects running where some initialization logic is @@ -61,14 +66,18 @@ export default function QueryContent( { // would cause to override previous wanted changes. useEffect( () => { const newQuery = {}; - if ( ! query.perPage && postsPerPage ) { + // When we inherit from global query always need to set the `perPage` + // based on the reading settings. + if ( inherit && query.perPage !== postsPerPage ) { + newQuery.perPage = postsPerPage; + } else if ( ! query.perPage && postsPerPage ) { newQuery.perPage = postsPerPage; } if ( !! Object.keys( newQuery ).length ) { __unstableMarkNextChangeAsNotPersistent(); updateQuery( newQuery ); } - }, [ query.perPage ] ); + }, [ query.perPage, postsPerPage, inherit ] ); // We need this for multi-query block pagination. // Query parameters for each block are scoped to their ID. useEffect( () => { From 5d371bc7a24a2da6f3a9ba1b2120c8b4c075c5cc Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Mon, 19 Jun 2023 15:18:34 +0100 Subject: [PATCH 097/163] Improve logic in `render_block_core_template_part`. (#50636) * Improve logic in `render_block_core_template_part`. * Use a foreach loop. * Apply suggestions from code review Co-authored-by: George Mamadashvili --------- Co-authored-by: George Mamadashvili --- .../block-library/src/template-part/index.php | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index d3de7d0b3afbd5..84e3a985ec4410 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -18,11 +18,12 @@ function render_block_core_template_part( $attributes ) { $template_part_id = null; $content = null; $area = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; + $stylesheet = get_stylesheet(); if ( isset( $attributes['slug'] ) && isset( $attributes['theme'] ) && - get_stylesheet() === $attributes['theme'] + $stylesheet === $attributes['theme'] ) { $template_part_id = $attributes['theme'] . '//' . $attributes['slug']; $template_part_query = new WP_Query( @@ -65,16 +66,22 @@ function render_block_core_template_part( $attributes ) { } else { // Else, if the template part was provided by the active theme, // render the corresponding file content. - $parent_theme_folders = get_block_theme_folders( get_template() ); - $child_theme_folders = get_block_theme_folders( get_stylesheet() ); - $child_theme_part_file_path = get_theme_file_path( '/' . $child_theme_folders['wp_template_part'] . '/' . $attributes['slug'] . '.html' ); - $parent_theme_part_file_path = get_theme_file_path( '/' . $parent_theme_folders['wp_template_part'] . '/' . $attributes['slug'] . '.html' ); - $template_part_file_path = 0 === validate_file( $attributes['slug'] ) && file_exists( $child_theme_part_file_path ) ? $child_theme_part_file_path : $parent_theme_part_file_path; - if ( 0 === validate_file( $attributes['slug'] ) && file_exists( $template_part_file_path ) ) { - $content = file_get_contents( $template_part_file_path ); - $content = is_string( $content ) && '' !== $content - ? _inject_theme_attribute_in_block_template_content( $content ) - : ''; + if ( 0 === validate_file( $attributes['slug'] ) ) { + $themes = array( $stylesheet ); + $template = get_template(); + if ( $stylesheet !== $template ) { + $themes[] = $template; + } + + foreach ( $themes as $theme ) { + $theme_folders = get_block_theme_folders( $theme ); + $template_part_file_path = get_theme_file_path( '/' . $theme_folders['wp_template_part'] . '/' . $attributes['slug'] . '.html' ); + if ( file_exists( $template_part_file_path ) ) { + $content = (string) file_get_contents( $template_part_file_path ); + $content = '' !== $content ? _inject_theme_attribute_in_block_template_content( $content ) : ''; + break; + } + } } if ( '' !== $content && null !== $content ) { From 8989749a58b951b3932ac615613221d73e13064b Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Mon, 19 Jun 2023 17:01:15 +0200 Subject: [PATCH 098/163] [Mobile] - Fix crash when switching to the HTML Editor (#51650) * Mobile - HTML Editor - Fix an issue where the editor would crash if it was focused on a block since it was calling clearSelectedBlock twice. * Mobile - Editor test - Update test to use toBeVisible instead --- packages/edit-post/src/test/editor.native.js | 43 +++++++++++++++---- .../src/components/provider/index.native.js | 11 +---- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/packages/edit-post/src/test/editor.native.js b/packages/edit-post/src/test/editor.native.js index 5e8397d22b854f..ebc6cc8b5f8825 100644 --- a/packages/edit-post/src/test/editor.native.js +++ b/packages/edit-post/src/test/editor.native.js @@ -1,18 +1,25 @@ /** * External dependencies */ -import { act, render } from 'test/helpers'; +import { + act, + addBlock, + fireEvent, + getBlock, + initializeEditor, + render, + setupCoreBlocks, +} from 'test/helpers'; /** * WordPress dependencies */ -import { registerCoreBlocks } from '@wordpress/block-library'; -import RNReactNativeGutenbergBridge from '@wordpress/react-native-bridge'; +import RNReactNativeGutenbergBridge, { + subscribeParentToggleHTMLMode, +} from '@wordpress/react-native-bridge'; // Force register 'core/editor' store. import { store } from '@wordpress/editor'; // eslint-disable-line no-unused-vars -jest.mock( '../components/layout', () => () => 'Layout' ); - /** * Internal dependencies */ @@ -34,11 +41,9 @@ afterAll( () => { jest.useRealTimers(); } ); -describe( 'Editor', () => { - beforeAll( () => { - registerCoreBlocks(); - } ); +setupCoreBlocks(); +describe( 'Editor', () => { it( 'detects unsupported block and sends hasUnsupportedBlocks true to native', () => { RNReactNativeGutenbergBridge.editorDidMount = jest.fn(); @@ -56,6 +61,26 @@ describe( 'Editor', () => { RNReactNativeGutenbergBridge.editorDidMount ).toHaveBeenCalledWith( [ 'core/notablock' ] ); } ); + + it( 'toggles the editor from Visual to HTML mode', async () => { + // Arrange + let toggleMode; + subscribeParentToggleHTMLMode.mockImplementation( ( callback ) => { + toggleMode = callback; + } ); + const screen = await initializeEditor(); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + + toggleMode(); + + // Assert + const htmlEditor = await screen.findByLabelText( 'html-view-content' ); + expect( htmlEditor ).toBeVisible(); + } ); } ); // Utilities. diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js index 73648a90a84f71..5fd6a4cdbb888b 100644 --- a/packages/editor/src/components/provider/index.native.js +++ b/packages/editor/src/components/provider/index.native.js @@ -319,8 +319,6 @@ class NativeEditorProvider extends Component { const { mode, switchMode } = this.props; // Refresh html content first. this.serializeToNativeAction(); - // Make sure to blur the selected block and dismiss the keyboard. - this.props.clearSelectedBlock(); switchMode( mode === 'visual' ? 'text' : 'visual' ); } @@ -387,12 +385,8 @@ const ComposedNativeProvider = compose( [ withDispatch( ( dispatch ) => { const { editPost, resetEditorBlocks, updateEditorSettings } = dispatch( editorStore ); - const { - updateSettings, - clearSelectedBlock, - insertBlock, - replaceBlock, - } = dispatch( blockEditorStore ); + const { updateSettings, insertBlock, replaceBlock } = + dispatch( blockEditorStore ); const { switchEditorMode } = dispatch( editPostStore ); const { addEntities, receiveEntityRecords } = dispatch( coreStore ); const { createSuccessNotice } = dispatch( noticesStore ); @@ -401,7 +395,6 @@ const ComposedNativeProvider = compose( [ updateBlockEditorSettings: updateSettings, updateEditorSettings, addEntities, - clearSelectedBlock, insertBlock, createSuccessNotice, editTitle( title ) { From e30f3d9eba01d6debecaed3f715239a92ade69df Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 19 Jun 2023 16:43:43 +0100 Subject: [PATCH 099/163] Update Library panel footer (#51652) * Update button, add radius * Manage template parts * Reinstate 'all' --- .../src/components/sidebar-navigation-item/style.scss | 3 +++ .../components/sidebar-navigation-screen-templates/index.js | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-item/style.scss b/packages/edit-site/src/components/sidebar-navigation-item/style.scss index e9ec7ecf91909e..ce5b278daaebe2 100644 --- a/packages/edit-site/src/components/sidebar-navigation-item/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-item/style.scss @@ -4,6 +4,7 @@ padding: $grid-unit-10 6px $grid-unit-10 $grid-unit-20; border: none; min-height: $grid-unit-50; + border-radius: $radius-block-ui; &:hover, &:focus, @@ -22,6 +23,8 @@ &:is(a) { text-decoration: none; + display: flex; + align-items: center; &:focus { box-shadow: none; diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js index 3da819b132c2c1..d0e0abb2f7d3fe 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js @@ -161,7 +161,6 @@ export default function SidebarNavigationScreenTemplates() { { config[ postType ].labels.reusableBlocks } From a05b0159c4223e797c578e1fe42fd3a9d09fd578 Mon Sep 17 00:00:00 2001 From: Bart Kalisz Date: Mon, 19 Jun 2023 18:35:16 +0200 Subject: [PATCH 100/163] Performance tests: Make theme versions consistent cross-env (#50905) --- .wp-env.json | 4 +++- bin/plugin/commands/performance.js | 11 +++++++++-- .../specs/performance/front-end-block-theme.test.js | 3 +-- .../specs/performance/front-end-classic-theme.test.js | 2 +- .../e2e-tests/specs/performance/post-editor.test.js | 6 ++++++ .../e2e-tests/specs/performance/site-editor.test.js | 1 - 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.wp-env.json b/.wp-env.json index aa8dfaf3c0c4ab..79810b194b667c 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -8,7 +8,9 @@ "wp-content/plugins/gutenberg": ".", "wp-content/mu-plugins": "./packages/e2e-tests/mu-plugins", "wp-content/plugins/gutenberg-test-plugins": "./packages/e2e-tests/plugins", - "wp-content/themes/gutenberg-test-themes": "./test/gutenberg-test-themes" + "wp-content/themes/gutenberg-test-themes": "./test/gutenberg-test-themes", + "wp-content/themes/gutenberg-test-themes/twentytwentyone": "https://downloads.wordpress.org/theme/twentytwentyone.1.7.zip", + "wp-content/themes/gutenberg-test-themes/twentytwentythree": "https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip" } } } diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js index 57c6674500a221..95e74c851c1d6b 100644 --- a/bin/plugin/commands/performance.js +++ b/bin/plugin/commands/performance.js @@ -337,8 +337,6 @@ async function runPerformanceTests( branches, options ) { performanceTestDirectory, 'test/emptytheme' ), - 'https://downloads.wordpress.org/theme/twentytwentyone.1.7.zip', - 'https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip', ], env: { tests: { @@ -352,6 +350,15 @@ async function runPerformanceTests( branches, options ) { performanceTestDirectory, 'packages/e2e-tests/plugins' ), + 'wp-content/themes/gutenberg-test-themes': + path.join( + performanceTestDirectory, + 'test/gutenberg-test-themes' + ), + 'wp-content/themes/gutenberg-test-themes/twentytwentyone': + 'https://downloads.wordpress.org/theme/twentytwentyone.1.7.zip', + 'wp-content/themes/gutenberg-test-themes/twentytwentythree': + 'https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip', }, }, }, diff --git a/packages/e2e-tests/specs/performance/front-end-block-theme.test.js b/packages/e2e-tests/specs/performance/front-end-block-theme.test.js index 260a20ce64c4df..d1036b1fdc2021 100644 --- a/packages/e2e-tests/specs/performance/front-end-block-theme.test.js +++ b/packages/e2e-tests/specs/performance/front-end-block-theme.test.js @@ -16,13 +16,12 @@ describe( 'Front End Performance', () => { }; beforeAll( async () => { - await activateTheme( 'twentytwentythree' ); + await activateTheme( 'gutenberg-test-themes/twentytwentythree' ); await logout(); } ); afterAll( async () => { saveResultsFile( __filename, results ); - await activateTheme( 'twentytwentyone' ); } ); it( 'Report TTFB, LCP, and LCP-TTFB', async () => { diff --git a/packages/e2e-tests/specs/performance/front-end-classic-theme.test.js b/packages/e2e-tests/specs/performance/front-end-classic-theme.test.js index ffe6906a529d8c..d687f7efdf80ff 100644 --- a/packages/e2e-tests/specs/performance/front-end-classic-theme.test.js +++ b/packages/e2e-tests/specs/performance/front-end-classic-theme.test.js @@ -16,7 +16,7 @@ describe( 'Front End Performance', () => { }; beforeAll( async () => { - await activateTheme( 'twentytwentyone' ); + await activateTheme( 'gutenberg-test-themes/twentytwentyone' ); await logout(); } ); diff --git a/packages/e2e-tests/specs/performance/post-editor.test.js b/packages/e2e-tests/specs/performance/post-editor.test.js index ee77f122a42bd4..9394d44619890b 100644 --- a/packages/e2e-tests/specs/performance/post-editor.test.js +++ b/packages/e2e-tests/specs/performance/post-editor.test.js @@ -7,6 +7,7 @@ import path from 'path'; * WordPress dependencies */ import { + activateTheme, createNewPost, saveDraft, insertBlock, @@ -83,6 +84,11 @@ describe( 'Post Editor Performance', () => { let traceResults; + beforeAll( async () => { + // See https://github.com/WordPress/gutenberg/pull/50905/files#r1209014677; + await activateTheme( 'gutenberg-test-themes/twentytwentyone' ); + } ); + afterAll( async () => { saveResultsFile( __filename, results ); deleteFile( traceFilePath ); diff --git a/packages/e2e-tests/specs/performance/site-editor.test.js b/packages/e2e-tests/specs/performance/site-editor.test.js index c141c2dbf58672..a1a2fa38e73cb7 100644 --- a/packages/e2e-tests/specs/performance/site-editor.test.js +++ b/packages/e2e-tests/specs/performance/site-editor.test.js @@ -87,7 +87,6 @@ describe( 'Site Editor Performance', () => { saveResultsFile( __filename, results ); await deleteAllTemplates( 'wp_template' ); await deleteAllTemplates( 'wp_template_part' ); - await activateTheme( 'twentytwentyone' ); } ); // Number of loading measurements to take. From 9cd88c73fd84fe103c487effb9aa65d753a78f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20K=C3=A4gy?= Date: Mon, 19 Jun 2023 20:31:50 +0200 Subject: [PATCH 101/163] Fix refactor flat term selector to use data api for creating new terms (#50952) --- package-lock.json | 1 - packages/editor/package.json | 1 - .../post-taxonomies/flat-term-selector.js | 50 ++++++++----------- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index b896036a811f19..513ed45ec9f824 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17863,7 +17863,6 @@ "@wordpress/wordcount": "file:packages/wordcount", "classnames": "^2.3.1", "date-fns": "^2.28.0", - "escape-html": "^1.0.3", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0", "rememo": "^4.0.2", diff --git a/packages/editor/package.json b/packages/editor/package.json index b6500fca6072b6..7ad03e43178eac 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -61,7 +61,6 @@ "@wordpress/wordcount": "file:../wordcount", "classnames": "^2.3.1", "date-fns": "^2.28.0", - "escape-html": "^1.0.3", "memize": "^2.1.0", "react-autosize-textarea": "^7.1.0", "rememo": "^4.0.2", diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index b586c07c80d4c9..9aba183a7e4af7 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import escapeHtml from 'escape-html'; - /** * WordPress dependencies */ @@ -12,7 +7,6 @@ import { FormTokenField, withFilters } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { useDebounce } from '@wordpress/compose'; -import apiFetch from '@wordpress/api-fetch'; import { speak } from '@wordpress/a11y'; /** @@ -51,28 +45,6 @@ const termNamesToIds = ( names, terms ) => { ); }; -// Tries to create a term or fetch it if it already exists. -function findOrCreateTerm( termName, restBase, namespace ) { - const escapedTermName = escapeHtml( termName ); - - return apiFetch( { - path: `/${ namespace }/${ restBase }`, - method: 'POST', - data: { name: escapedTermName }, - } ) - .catch( ( error ) => { - if ( error.code !== 'term_exists' ) { - return Promise.reject( error ); - } - - return Promise.resolve( { - id: error.data.term_id, - name: termName, - } ); - } ) - .then( unescapeTerm ); -} - export function FlatTermSelector( { slug } ) { const [ values, setValues ] = useState( [] ); const [ search, setSearch ] = useState( '' ); @@ -165,11 +137,30 @@ export function FlatTermSelector( { slug } ) { }, [ searchResults ] ); const { editPost } = useDispatch( editorStore ); + const { saveEntityRecord } = useDispatch( coreStore ); if ( ! hasAssignAction ) { return null; } + async function findOrCreateTerm( term ) { + try { + const newTerm = await saveEntityRecord( 'taxonomy', slug, term, { + throwOnError: true, + } ); + return unescapeTerm( newTerm ); + } catch ( error ) { + if ( error.code !== 'term_exists' ) { + throw error; + } + + return { + id: error.data.term_id, + name: term.name, + }; + } + } + function onUpdateTerms( newTermIds ) { editPost( { [ taxonomy.rest_base ]: newTermIds } ); } @@ -209,10 +200,9 @@ export function FlatTermSelector( { slug } ) { return; } - const namespace = taxonomy?.rest_namespace ?? 'wp/v2'; Promise.all( newTermNames.map( ( termName ) => - findOrCreateTerm( termName, taxonomy.rest_base, namespace ) + findOrCreateTerm( { name: termName } ) ) ).then( ( newTerms ) => { const newAvailableTerms = availableTerms.concat( newTerms ); From 32d394c3518fccdc8ce57d8d3ed8f0ff8cf33ff3 Mon Sep 17 00:00:00 2001 From: Juan Aldasoro Date: Mon, 19 Jun 2023 23:41:06 +0200 Subject: [PATCH 102/163] Page Details View: Show featured image only if there is one (#51649) --- .../sidebar-navigation-screen-page/index.js | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js index 897c647cddae49..536da766b668c9 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ @@ -104,30 +99,20 @@ export default function SidebarNavigationScreenPage() { } content={ <> - -
    - { !! featuredMediaSourceUrl && ( +
    { - ) } - { ! record?.featured_media && ( -

    { __( 'No featured image' ) }

    - ) } -
    - +
    +
    + ) } { !! record?.excerpt?.rendered && ( Date: Tue, 20 Jun 2023 09:24:36 +0800 Subject: [PATCH 103/163] Distraction Free: Avoid focus loss when enabling/disabling distraction free mode via the more menu (#51627) * Avoid header region unmounting when enabling distraction free mode via the more menu * remove the conditional in framer motion state for header elements, remove block selection workaround for lost focus --------- Co-authored-by: Andrei Draganescu --- .../edit-post/src/components/header/index.js | 9 ++--- .../components/header/writing-menu/index.js | 10 ------ .../components/interface-skeleton/index.js | 36 ++++++++++++------- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index 3306a0fdf1606a..27fff18be8d6fe 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -27,7 +27,6 @@ function Header( { setEntitiesSavedStatesCallback } ) { isPublishSidebarOpened, isSaving, showIconLabels, - isDistractionFreeMode, } = useSelect( ( select ) => ( { hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), @@ -36,21 +35,17 @@ function Header( { setEntitiesSavedStatesCallback } ) { isSaving: select( editPostStore ).isSavingMetaBoxes(), showIconLabels: select( editPostStore ).isFeatureActive( 'showIconLabels' ), - isDistractionFreeMode: - select( editPostStore ).isFeatureActive( 'distractionFree' ), } ), [] ); - const isDistractionFree = isDistractionFreeMode && isLargeViewport; - const slideY = { - hidden: isDistractionFree ? { y: '-50' } : { y: 0 }, + hidden: { y: '-50px' }, hover: { y: 0, transition: { type: 'tween', delay: 0.2 } }, }; const slideX = { - hidden: isDistractionFree ? { x: '-100%' } : { x: 0 }, + hidden: { x: '-100%' }, hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, }; diff --git a/packages/edit-post/src/components/header/writing-menu/index.js b/packages/edit-post/src/components/header/writing-menu/index.js index 6cea56392381e1..c0d6ff994815e6 100644 --- a/packages/edit-post/src/components/header/writing-menu/index.js +++ b/packages/edit-post/src/components/header/writing-menu/index.js @@ -25,26 +25,16 @@ function WritingMenu() { [] ); - const blocks = useSelect( - ( select ) => select( blockEditorStore ).getBlocks(), - [] - ); - const { setIsInserterOpened, setIsListViewOpened, closeGeneralSidebar } = useDispatch( postEditorStore ); const { set: setPreference } = useDispatch( preferencesStore ); - const { selectBlock } = useDispatch( blockEditorStore ); - const toggleDistractionFree = () => { registry.batch( () => { setPreference( 'core/edit-post', 'fixedToolbar', false ); setIsInserterOpened( false ); setIsListViewOpened( false ); closeGeneralSidebar(); - if ( ! isDistractionFree && !! blocks.length ) { - selectBlock( blocks[ 0 ].clientId ); - } } ); }; diff --git a/packages/interface/src/components/interface-skeleton/index.js b/packages/interface/src/components/interface-skeleton/index.js index fe329a75d43e07..43e4e532e8eab3 100644 --- a/packages/interface/src/components/interface-skeleton/index.js +++ b/packages/interface/src/components/interface-skeleton/index.js @@ -75,11 +75,12 @@ function InterfaceSkeleton( const mergedLabels = { ...defaultLabels, ...labels }; const headerVariants = { - hidden: isDistractionFree ? { opacity: 0 } : { opacity: 1 }, + hidden: { opacity: 0 }, hover: { opacity: 1, transition: { type: 'tween', delay: 0.2, delayChildren: 0.2 }, }, + distractionFreeInactive: { opacity: 1, transition: { delay: 0 } }, }; return ( @@ -97,23 +98,32 @@ function InterfaceSkeleton( ) } >
    - { !! header && isDistractionFree && ( + { !! header && ( - { header } - - ) } - { !! header && ! isDistractionFree && ( - { header } From c5d266085ce3372709d6fd62388de25417d81e5a Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 20 Jun 2023 11:33:32 +1000 Subject: [PATCH 104/163] Adding missing changelog from #51516 (#51668) --- packages/block-editor/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 3a29874d5dd57b..37aa57a852d32d 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fix + +- Fluid typography: custom font-sizes should use max viewport width ([#51516](https://github.com/WordPress/gutenberg/pull/51516)). + ## 12.3.0 (2023-06-07) ## 12.2.0 (2023-05-24) From 7efbf84d43bd033cf689dd11f16177ce98ff87ba Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 20 Jun 2023 11:54:03 +1000 Subject: [PATCH 105/163] Site editor sidebar: home template details (#51223) * Initial commit: - refactoring page details so that the styles and components can be used in templates - getting home template details in order * - displaying template areas - refactoring footer to show last modified in template and page details * - bare bones rest controller changes to return modified for `get_item` - linking up template areas * - passing footer class to row * - hooking into settings. * Refactoring input controls layout Tweaking CSS accordingly * Reverted prefix change to file that was not copied to GB * Removing last modified changes until the templates API supports it. We can reinstate these changes once it's merged (also adding the property to core-data/src/entity-types/wp-template-part.ts) * Showing the details pages for the index template Updating translations for entity types when saving * Fixed new unlock path * updating design of area buttons switching over site title editing to posts page title editing * Updated hover states of area buttons * This commit: - adds a last updated footer (if the modified property is available) - removes all controls and addes details * Don't need these * Reinstate post title and posts per page controls Reinstate allow comments control Abstract last modified footer * Updated copy * Update help text * SidebarNavigationItem instead of button for links to template parts from the home template * Wrap areas in ItemGroup * Remove bottom margin on last detail panel * Spacing * Large inputs * Leave border radius on inputs as 2px for now * Use NumberControl * Remove debounce Use spin custom controls on the number control component Update changelog * Restore since annotation change made in https://github.com/WordPress/gutenberg/pull/51362 --------- Co-authored-by: James Koster --- .../core-data/src/entity-types/wp-template.ts | 4 + packages/edit-site/CHANGELOG.md | 3 + .../index.js | 39 +++ .../style.scss | 5 + .../index.js | 40 ++++ ...r-navigation-screen-details-panel-label.js | 14 ++ ...bar-navigation-screen-details-panel-row.js | 29 +++ ...r-navigation-screen-details-panel-value.js | 14 ++ .../style.scss | 25 ++ .../sidebar-navigation-screen-page/index.js | 35 +-- .../page-details.js | 34 ++- .../sidebar-navigation-screen-page/style.scss | 19 +- .../home-template-details.js | 223 ++++++++++++++++++ .../index.js | 25 +- .../style.scss | 23 ++ .../sidebar-navigation-screen/style.scss | 22 ++ .../sidebar-navigation-subtitle/index.js | 5 - .../sidebar-navigation-subtitle/style.scss | 7 - packages/edit-site/src/style.scss | 3 +- .../hooks/use-is-dirty.js | 2 + 20 files changed, 487 insertions(+), 84 deletions(-) create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-details-footer/style.scss create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-details-panel/index.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-label.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-row.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-value.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js delete mode 100644 packages/edit-site/src/components/sidebar-navigation-subtitle/index.js delete mode 100644 packages/edit-site/src/components/sidebar-navigation-subtitle/style.scss diff --git a/packages/core-data/src/entity-types/wp-template.ts b/packages/core-data/src/entity-types/wp-template.ts index 544476bbb7f36e..ac6db09035f193 100644 --- a/packages/core-data/src/entity-types/wp-template.ts +++ b/packages/core-data/src/entity-types/wp-template.ts @@ -85,6 +85,10 @@ declare module './base-entity-records' { * Whether a template is a custom template. */ is_custom: Record< string, string >; + /** + * The date the template was last modified, in the site's timezone. + */ + modified: ContextualField< string, 'view' | 'edit', C >; } } } diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index 0b81a94ee6b1ec..808554a1c5b1ee 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Enhancements +- Site editor sidebar: add home template details and controls [#51223](https://github.com/WordPress/gutenberg/pull/51223). + ## 5.12.0 (2023-06-07) ## 5.11.0 (2023-05-24) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js new file mode 100644 index 00000000000000..4b5c4fb7de8808 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js @@ -0,0 +1,39 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { humanTimeDiff } from '@wordpress/date'; +import { createInterpolateElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { + SidebarNavigationScreenDetailsPanelRow, + SidebarNavigationScreenDetailsPanelLabel, + SidebarNavigationScreenDetailsPanelValue, +} from '../sidebar-navigation-screen-details-panel'; + +export default function SidebarNavigationScreenDetailsFooter( { + lastModifiedDateTime, +} ) { + return ( + + + { __( 'Last modified' ) } + + + { createInterpolateElement( + sprintf( + /* translators: %s: is the relative time when the post was last modified. */ + __( '' ), + humanTimeDiff( lastModifiedDateTime ) + ), + { + time: + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/style.scss new file mode 100644 index 00000000000000..fcd20d64696eb5 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/style.scss @@ -0,0 +1,5 @@ +.edit-site-sidebar-navigation-screen-details-footer { + padding-top: $grid-unit-10; + padding-bottom: $grid-unit-10; + padding-left: $grid-unit-20; +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/index.js new file mode 100644 index 00000000000000..f95ec59b4d80e9 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/index.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { + __experimentalVStack as VStack, + __experimentalHeading as Heading, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import SidebarNavigationScreenDetailsPanelLabel from './sidebar-navigation-screen-details-panel-label'; +import SidebarNavigationScreenDetailsPanelRow from './sidebar-navigation-screen-details-panel-row'; +import SidebarNavigationScreenDetailsPanelValue from './sidebar-navigation-screen-details-panel-value'; + +function SidebarNavigationScreenDetailsPanel( { title, children, spacing } ) { + return ( + + { title && ( + + { title } + + ) } + { children } + + ); +} + +export { + SidebarNavigationScreenDetailsPanel, + SidebarNavigationScreenDetailsPanelRow, + SidebarNavigationScreenDetailsPanelLabel, + SidebarNavigationScreenDetailsPanelValue, +}; diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-label.js b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-label.js new file mode 100644 index 00000000000000..157eecd557519c --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-label.js @@ -0,0 +1,14 @@ +/** + * WordPress dependencies + */ +import { __experimentalText as Text } from '@wordpress/components'; + +export default function SidebarNavigationScreenDetailsPanelLabel( { + children, +} ) { + return ( + + { children } + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-row.js b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-row.js new file mode 100644 index 00000000000000..541e654b0933ed --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-row.js @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __experimentalHStack as HStack } from '@wordpress/components'; + +export default function SidebarNavigationScreenDetailsPanelRow( { + label, + children, + className, +} ) { + return ( + + { children } + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-value.js b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-value.js new file mode 100644 index 00000000000000..80e8ba8cf1d538 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/sidebar-navigation-screen-details-panel-value.js @@ -0,0 +1,14 @@ +/** + * WordPress dependencies + */ +import { __experimentalText as Text } from '@wordpress/components'; + +export default function SidebarNavigationScreenDetailsPanelValue( { + children, +} ) { + return ( + + { children } + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss new file mode 100644 index 00000000000000..e94894c0d6ea39 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-panel/style.scss @@ -0,0 +1,25 @@ +.edit-site-sidebar-navigation-details-screen-panel { + margin-bottom: $grid-unit-30; + + &:last-of-type { + margin-bottom: 0; + } + + .edit-site-sidebar-navigation-details-screen-panel__heading { + color: $gray-400; + text-transform: uppercase; + font-weight: 500; + font-size: 11px; + padding: 0; + margin-bottom: 0; + } +} + +.edit-site-sidebar-navigation-details-screen-panel__label.edit-site-sidebar-navigation-details-screen-panel__label { + color: $gray-600; + width: 100px; +} + +.edit-site-sidebar-navigation-details-screen-panel__value.edit-site-sidebar-navigation-details-screen-panel__value { + color: $gray-200; +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js index 536da766b668c9..55ca9b646f423b 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js @@ -1,21 +1,17 @@ /** * WordPress dependencies */ -import { __, sprintf } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import { useDispatch, useSelect } from '@wordpress/data'; import { __experimentalUseNavigator as useNavigator, __experimentalVStack as VStack, ExternalLink, __experimentalTruncate as Truncate, - __experimentalHStack as HStack, - __experimentalText as Text, } from '@wordpress/components'; import { store as coreStore, useEntityRecord } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; import { pencil } from '@wordpress/icons'; -import { humanTimeDiff } from '@wordpress/date'; -import { createInterpolateElement } from '@wordpress/element'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; import { escapeAttribute } from '@wordpress/escape-html'; @@ -26,9 +22,9 @@ import SidebarNavigationScreen from '../sidebar-navigation-screen'; import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; import SidebarButton from '../sidebar-button'; -import SidebarNavigationSubtitle from '../sidebar-navigation-subtitle'; import PageDetails from './page-details'; import PageActions from '../page-actions'; +import SidebarNavigationScreenDetailsFooter from '../sidebar-navigation-screen-details-footer'; export default function SidebarNavigationScreenPage() { const navigator = useNavigator(); @@ -121,35 +117,14 @@ export default function SidebarNavigationScreenPage() { { stripHTML( record.excerpt.rendered ) } ) } - - { __( 'Details' ) } - } footer={ !! record?.modified && ( - - - { __( 'Last modified' ) } - - - { createInterpolateElement( - sprintf( - /* translators: %s: is the relative time when the post was last modified. */ - __( '' ), - humanTimeDiff( record.modified ) - ), - { - time: - + ) } /> diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js index 290ae4907f9a9e..56a94dc64a2a97 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js @@ -2,12 +2,7 @@ * WordPress dependencies */ import { __, _x, sprintf } from '@wordpress/i18n'; -import { - __experimentalHStack as HStack, - __experimentalText as Text, - __experimentalVStack as VStack, - __experimentalTruncate as Truncate, -} from '@wordpress/components'; +import { __experimentalTruncate as Truncate } from '@wordpress/components'; import { count as wordCount } from '@wordpress/wordcount'; import { useSelect } from '@wordpress/data'; import { decodeEntities } from '@wordpress/html-entities'; @@ -19,6 +14,12 @@ import { store as coreStore, useEntityRecord } from '@wordpress/core-data'; import StatusLabel from './status-label'; import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; +import { + SidebarNavigationScreenDetailsPanel, + SidebarNavigationScreenDetailsPanelRow, + SidebarNavigationScreenDetailsPanelLabel, + SidebarNavigationScreenDetailsPanelValue, +} from '../sidebar-navigation-screen-details-panel'; // Taken from packages/editor/src/components/time-to-read/index.js. const AVERAGE_READING_RATE = 189; @@ -138,26 +139,21 @@ export default function PageDetails( { id } ) { [ record?.parent ] ); return ( - + { getPageDetails( { parentTitle, templateTitle, ...record, } ).map( ( { label, value } ) => ( - - + + { label } - - + + { value } - - + + ) ) } - + ); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss index c58e9f09392465..66efe31726e8b7 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/style.scss @@ -30,6 +30,7 @@ .edit-site-sidebar-navigation-screen-page__excerpt { font-size: $helptext-font-size; + margin-bottom: $grid-unit-30; } .edit-site-sidebar-navigation-screen-page__modified { @@ -60,21 +61,3 @@ fill: $alert-green; } } - -.edit-site-sidebar-navigation-screen-page__footer { - padding-top: $grid-unit-10; - padding-bottom: $grid-unit-10; - padding-left: $grid-unit-20; -} - -.edit-site-sidebar-navigation-screen-page__details { - .edit-site-sidebar-navigation-screen-page__details-label { - color: $gray-600; - width: 100px; - } - - .edit-site-sidebar-navigation-screen-page__details-value.edit-site-sidebar-navigation-screen-page__details-value { - color: $gray-200; - } -} - diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js new file mode 100644 index 00000000000000..51c3d7e5f78e9a --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js @@ -0,0 +1,223 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { debounce } from '@wordpress/compose'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { + CheckboxControl, + __experimentalUseNavigator as useNavigator, + __experimentalInputControl as InputControl, + __experimentalNumberControl as NumberControl, + __experimentalTruncate as Truncate, + __experimentalItemGroup as ItemGroup, +} from '@wordpress/components'; +import { header, footer, layout } from '@wordpress/icons'; +import { useMemo, useState, useEffect } from '@wordpress/element'; +import { decodeEntities } from '@wordpress/html-entities'; + +/** + * Internal dependencies + */ +import { + SidebarNavigationScreenDetailsPanel, + SidebarNavigationScreenDetailsPanelRow, +} from '../sidebar-navigation-screen-details-panel'; +import { unlock } from '../../lock-unlock'; +import { store as editSiteStore } from '../../store'; +import { useLink } from '../routes/link'; +import SidebarNavigationItem from '../sidebar-navigation-item'; + +const EMPTY_OBJECT = {}; + +function TemplateAreaButton( { postId, icon, title } ) { + const icons = { + header, + footer, + }; + const linkInfo = useLink( { + postType: 'wp_template_part', + postId, + } ); + + return ( + + + { decodeEntities( title ) } + + + ); +} + +export default function HomeTemplateDetails() { + const navigator = useNavigator(); + const { + params: { postType, postId }, + } = navigator; + const { editEntityRecord } = useDispatch( coreStore ); + + const { + allowCommentsOnNewPosts, + templatePartAreas, + postsPerPage, + postsPageTitle, + postsPageId, + templateRecord, + } = useSelect( + ( select ) => { + const { getEntityRecord } = select( coreStore ); + const siteSettings = getEntityRecord( 'root', 'site' ); + const { getSettings } = unlock( select( editSiteStore ) ); + const siteEditorSettings = getSettings(); + const _templateRecord = + select( coreStore ).getEditedEntityRecord( + 'postType', + postType, + postId + ) || EMPTY_OBJECT; + const _postsPageRecord = siteSettings?.page_for_posts + ? select( coreStore ).getEntityRecord( + 'postType', + 'page', + siteSettings?.page_for_posts + ) + : EMPTY_OBJECT; + + return { + templateRecord: _templateRecord, + allowCommentsOnNewPosts: + siteSettings?.default_comment_status === 'open', + postsPageTitle: _postsPageRecord?.title?.rendered, + postsPageId: _postsPageRecord?.id, + postsPerPage: siteSettings?.posts_per_page, + templatePartAreas: siteEditorSettings?.defaultTemplatePartAreas, + }; + }, + [ postType, postId ] + ); + + const [ commentsOnNewPostsValue, setCommentsOnNewPostsValue ] = + useState( '' ); + const [ postsCountValue, setPostsCountValue ] = useState( 1 ); + const [ postsPageTitleValue, setPostsPageTitleValue ] = useState( '' ); + + useEffect( () => { + setCommentsOnNewPostsValue( allowCommentsOnNewPosts ); + setPostsPageTitleValue( postsPageTitle ); + setPostsCountValue( postsPerPage ); + }, [ postsPageTitle, allowCommentsOnNewPosts, postsPerPage ] ); + + const templateAreas = useMemo( () => { + return templateRecord?.blocks && templatePartAreas + ? templateRecord.blocks + .filter( ( { name } ) => name === 'core/template-part' ) + .map( ( { attributes } ) => ( { + ...templatePartAreas?.find( + ( { area } ) => area === attributes?.tagName + ), + ...attributes, + } ) ) + : []; + }, [ templateRecord?.blocks, templatePartAreas ] ); + + const setAllowCommentsOnNewPosts = ( newValue ) => { + setCommentsOnNewPostsValue( newValue ); + editEntityRecord( 'root', 'site', undefined, { + default_comment_status: newValue ? 'open' : null, + } ); + }; + + const setPostsPageTitle = ( newValue ) => { + setPostsPageTitleValue( newValue ); + editEntityRecord( 'postType', 'page', postsPageId, { + title: newValue, + } ); + }; + + const setPostsPerPage = ( newValue ) => { + setPostsCountValue( newValue ); + editEntityRecord( 'root', 'site', undefined, { + posts_per_page: newValue, + } ); + }; + + return ( + <> + + { postsPageId && ( + + + + ) } + + + + + + + + + + + + + { templateAreas.map( ( { label, icon, theme, slug } ) => ( + + + + ) ) } + + + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js index ecc07deceed35f..3b1bdf71fa99f0 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js @@ -9,7 +9,6 @@ import { Icon, } from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; - /** * Internal dependencies */ @@ -20,8 +19,10 @@ import { store as editSiteStore } from '../../store'; import SidebarButton from '../sidebar-button'; import { useAddedBy } from '../list/added-by'; import TemplateActions from '../template-actions'; +import HomeTemplateDetails from './home-template-details'; +import SidebarNavigationScreenDetailsFooter from '../sidebar-navigation-screen-details-footer'; -function useTemplateTitleAndDescription( postType, postId ) { +function useTemplateDetails( postType, postId ) { const { getDescription, getTitle, record } = useEditedEntityRecord( postType, postId @@ -42,6 +43,20 @@ function useTemplateTitleAndDescription( postType, postId ) { ); } + let content = null; + if ( record?.slug === 'home' || record?.slug === 'index' ) { + content = ; + } + + let footer = null; + if ( !! record?.modified ) { + footer = ( + + ); + } + const description = ( <> { descriptionText } @@ -74,7 +89,7 @@ function useTemplateTitleAndDescription( postType, postId ) { ); - return { title, description }; + return { title, description, content, footer }; } export default function SidebarNavigationScreenTemplate() { @@ -83,7 +98,7 @@ export default function SidebarNavigationScreenTemplate() { params: { postType, postId }, } = navigator; const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); - const { title, description } = useTemplateTitleAndDescription( + const { title, content, description, footer } = useTemplateDetails( postType, postId ); @@ -109,6 +124,8 @@ export default function SidebarNavigationScreenTemplate() { } description={ description } + content={ content } + footer={ footer } /> ); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss index e86c137a574d5f..8b1cf5bbf456ca 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/style.scss @@ -23,3 +23,26 @@ } } } + +.edit-site-sidebar-navigation-screen-template__template-area-button { + color: $white; + display: flex; + align-items: center; + width: 100%; + flex-wrap: nowrap; + border-radius: 4px; + &:hover, + &:focus { + background: $gray-800; + color: $white; + } +} + +.edit-site-sidebar-navigation-screen-template__template-area-label-text { + margin: 0 $grid-unit-20 0 $grid-unit-05; + flex-grow: 1; +} + +.edit-site-sidebar-navigation-screen-template__template-icon { + display: flex; +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index 9b54e06ca77e72..417dc7d6b1f059 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -97,3 +97,25 @@ border-top: 1px solid $gray-800; } +/* In general style overrides are discouraged. + * This is a temporary solution to override the InputControl component's styles. + * The `Theme` component will potentially be the more appropriate approach + * once that component is stabilized. + * See: packages/components/src/theme + */ +.edit-site-sidebar-navigation-screen__input-control { + width: 100%; + .components-input-control__container { + background: transparent; + } + .components-input-control__input { + color: $gray-200 !important; + background: $gray-800 !important; + } + .components-input-control__backdrop { + border: 4px !important; + } + .components-base-control__help { + color: $gray-600; + } +} diff --git a/packages/edit-site/src/components/sidebar-navigation-subtitle/index.js b/packages/edit-site/src/components/sidebar-navigation-subtitle/index.js deleted file mode 100644 index 2a20f31ce7fb48..00000000000000 --- a/packages/edit-site/src/components/sidebar-navigation-subtitle/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export default function SidebarNavigationSubtitle( { children } ) { - return ( -

    { children }

    - ); -} diff --git a/packages/edit-site/src/components/sidebar-navigation-subtitle/style.scss b/packages/edit-site/src/components/sidebar-navigation-subtitle/style.scss deleted file mode 100644 index b7ff9faba49f38..00000000000000 --- a/packages/edit-site/src/components/sidebar-navigation-subtitle/style.scss +++ /dev/null @@ -1,7 +0,0 @@ -.edit-site-sidebar-navigation-subtitle { - color: $gray-400; - text-transform: uppercase; - font-weight: 500; - font-size: 11px; - padding: $grid-unit-10 0; -} diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 916d8291532449..5f434bb84f8f7c 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -29,11 +29,12 @@ @import "./components/sidebar-button/style.scss"; @import "./components/sidebar-navigation-item/style.scss"; @import "./components/sidebar-navigation-screen/style.scss"; +@import "./components/sidebar-navigation-screen-details-footer/style.scss"; @import "./components/sidebar-navigation-screen-global-styles/style.scss"; @import "./components/sidebar-navigation-screen-navigation-menu/style.scss"; @import "./components/sidebar-navigation-screen-page/style.scss"; +@import "components/sidebar-navigation-screen-details-panel/style.scss"; @import "./components/sidebar-navigation-screen-template/style.scss"; -@import "./components/sidebar-navigation-subtitle/style.scss"; @import "./components/site-hub/style.scss"; @import "./components/sidebar-navigation-screen-navigation-menus/style.scss"; @import "./components/site-icon/style.scss"; diff --git a/packages/editor/src/components/entities-saved-states/hooks/use-is-dirty.js b/packages/editor/src/components/entities-saved-states/hooks/use-is-dirty.js index e1adc68c7a82ae..7906dd800c6610 100644 --- a/packages/editor/src/components/entities-saved-states/hooks/use-is-dirty.js +++ b/packages/editor/src/components/entities-saved-states/hooks/use-is-dirty.js @@ -13,6 +13,8 @@ const TRANSLATED_SITE_PROPERTIES = { site_icon: __( 'Icon' ), show_on_front: __( 'Show on front' ), page_on_front: __( 'Page on front' ), + posts_per_page: __( 'Maximum posts per page' ), + default_comment_status: __( 'Allow comments on new posts' ), }; export const useIsDirty = () => { From 146481457586a17987435290b0626be0ad280a5a Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 20 Jun 2023 13:55:11 +1000 Subject: [PATCH 106/163] Site editor sidebar: add footer to template part and ensure nested template areas display (#51669) * This commit: - performs `lastModifiedDateTime` check in the generic footer - adds a last modified footer to template parts - ensures we know which default template part areas a template is using, even if those template areas are nested * cha cha chaaaaaaange logs of change * Adding spacing to page details --- packages/edit-site/CHANGELOG.md | 1 + .../index.js | 40 ++++++------ .../sidebar-navigation-screen-page/index.js | 8 +-- .../page-details.js | 5 +- .../index.js | 15 +++-- .../home-template-details.js | 61 +++++++++++-------- .../index.js | 21 +++---- 7 files changed, 85 insertions(+), 66 deletions(-) diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index 808554a1c5b1ee..c19db6d3176497 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -4,6 +4,7 @@ ### Enhancements - Site editor sidebar: add home template details and controls [#51223](https://github.com/WordPress/gutenberg/pull/51223). +- Site editor sidebar: add footer to template part and ensure nested template areas display [#51669](https://github.com/WordPress/gutenberg/pull/51669). ## 5.12.0 (2023-06-07) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js index 4b5c4fb7de8808..f9d7f112a41d5f 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-details-footer/index.js @@ -18,22 +18,28 @@ export default function SidebarNavigationScreenDetailsFooter( { lastModifiedDateTime, } ) { return ( - - - { __( 'Last modified' ) } - - - { createInterpolateElement( - sprintf( - /* translators: %s: is the relative time when the post was last modified. */ - __( '' ), - humanTimeDiff( lastModifiedDateTime ) - ), - { - time: - + <> + { lastModifiedDateTime && ( + + + { __( 'Last modified' ) } + + + { createInterpolateElement( + sprintf( + /* translators: %s: is the relative time when the post was last modified. */ + __( '' ), + humanTimeDiff( lastModifiedDateTime ) + ), + { + time: ( + + + ) } + ); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js index 55ca9b646f423b..8e9903be0fa5e2 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js @@ -121,11 +121,9 @@ export default function SidebarNavigationScreenPage() { } footer={ - !! record?.modified && ( - - ) + } /> ) : null; diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js index 56a94dc64a2a97..0b8b459e482e6f 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js @@ -139,7 +139,10 @@ export default function PageDetails( { id } ) { [ record?.parent ] ); return ( - + { getPageDetails( { parentTitle, templateTitle, diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template-part/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-template-part/index.js index 7f6344b3292220..664c708792dc9b 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template-part/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template-part/index.js @@ -19,10 +19,10 @@ import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; import SidebarButton from '../sidebar-button'; import { useAddedBy } from '../list/added-by'; - +import SidebarNavigationScreenDetailsFooter from '../sidebar-navigation-screen-details-footer'; import TemplatePartNavigationMenus from './template-part-navigation-menus'; -function useTemplateTitleAndDescription( postType, postId ) { +function useTemplateDetails( postType, postId ) { const { getDescription, getTitle, record } = useEditedEntityRecord( postType, postId @@ -78,7 +78,13 @@ function useTemplateTitleAndDescription( postType, postId ) { ); - return { title, description }; + const footer = !! record?.modified ? ( + + ) : null; + + return { title, description, footer }; } export default function SidebarNavigationScreenTemplatePart() { @@ -88,7 +94,7 @@ export default function SidebarNavigationScreenTemplatePart() { const { record } = useEditedEntityRecord( postType, postId ); - const { title, description } = useTemplateTitleAndDescription( + const { title, description, footer } = useTemplateDetails( postType, postId ); @@ -118,6 +124,7 @@ export default function SidebarNavigationScreenTemplatePart() { content={ } + footer={ footer } /> ); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js index 51c3d7e5f78e9a..c740a1baf37c36 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/home-template-details.js @@ -73,19 +73,15 @@ export default function HomeTemplateDetails() { postsPerPage, postsPageTitle, postsPageId, - templateRecord, + currentTemplateParts, } = useSelect( ( select ) => { const { getEntityRecord } = select( coreStore ); const siteSettings = getEntityRecord( 'root', 'site' ); const { getSettings } = unlock( select( editSiteStore ) ); + const _currentTemplateParts = + select( editSiteStore ).getCurrentTemplateTemplateParts(); const siteEditorSettings = getSettings(); - const _templateRecord = - select( coreStore ).getEditedEntityRecord( - 'postType', - postType, - postId - ) || EMPTY_OBJECT; const _postsPageRecord = siteSettings?.page_for_posts ? select( coreStore ).getEntityRecord( 'postType', @@ -95,13 +91,13 @@ export default function HomeTemplateDetails() { : EMPTY_OBJECT; return { - templateRecord: _templateRecord, allowCommentsOnNewPosts: siteSettings?.default_comment_status === 'open', postsPageTitle: _postsPageRecord?.title?.rendered, postsPageId: _postsPageRecord?.id, postsPerPage: siteSettings?.posts_per_page, templatePartAreas: siteEditorSettings?.defaultTemplatePartAreas, + currentTemplateParts: _currentTemplateParts, }; }, [ postType, postId ] @@ -112,24 +108,31 @@ export default function HomeTemplateDetails() { const [ postsCountValue, setPostsCountValue ] = useState( 1 ); const [ postsPageTitleValue, setPostsPageTitleValue ] = useState( '' ); + /* + * This hook serves to set the server-retrieved values, + * postsPageTitle, allowCommentsOnNewPosts, postsPerPage, + * to local state. + */ useEffect( () => { setCommentsOnNewPostsValue( allowCommentsOnNewPosts ); setPostsPageTitleValue( postsPageTitle ); setPostsCountValue( postsPerPage ); }, [ postsPageTitle, allowCommentsOnNewPosts, postsPerPage ] ); + /* + * Merge data in currentTemplateParts with templatePartAreas, + * which contains the template icon and fallback labels + */ const templateAreas = useMemo( () => { - return templateRecord?.blocks && templatePartAreas - ? templateRecord.blocks - .filter( ( { name } ) => name === 'core/template-part' ) - .map( ( { attributes } ) => ( { - ...templatePartAreas?.find( - ( { area } ) => area === attributes?.tagName - ), - ...attributes, - } ) ) + return currentTemplateParts.length && templatePartAreas + ? currentTemplateParts.map( ( { templatePart } ) => ( { + ...templatePartAreas?.find( + ( { area } ) => area === templatePart?.area + ), + ...templatePart, + } ) ) : []; - }, [ templateRecord?.blocks, templatePartAreas ] ); + }, [ currentTemplateParts, templatePartAreas ] ); const setAllowCommentsOnNewPosts = ( newValue ) => { setCommentsOnNewPostsValue( newValue ); @@ -207,15 +210,19 @@ export default function HomeTemplateDetails() { spacing={ 3 } > - { templateAreas.map( ( { label, icon, theme, slug } ) => ( - - - - ) ) } + { templateAreas.map( + ( { label, icon, theme, slug, title } ) => ( + + + + ) + ) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js index 3b1bdf71fa99f0..23f758b6f7c571 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js @@ -43,19 +43,16 @@ function useTemplateDetails( postType, postId ) { ); } - let content = null; - if ( record?.slug === 'home' || record?.slug === 'index' ) { - content = ; - } + const content = + record?.slug === 'home' || record?.slug === 'index' ? ( + + ) : null; - let footer = null; - if ( !! record?.modified ) { - footer = ( - - ); - } + const footer = !! record?.modified ? ( + + ) : null; const description = ( <> From 3c4ad8d0e9ac7182aced9731756bfe7646a4a5d8 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 20 Jun 2023 08:05:08 +0400 Subject: [PATCH 107/163] Playwright Utils: Change preference update method in setIsFixedToolbar (#51659) --- .../src/editor/set-is-fixed-toolbar.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts b/packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts index 2f66fac8692618..0e642a1de76626 100644 --- a/packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts +++ b/packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts @@ -11,11 +11,8 @@ import type { Editor } from './index'; */ export async function setIsFixedToolbar( this: Editor, isFixed: boolean ) { await this.page.evaluate( ( _isFixed ) => { - const { select, dispatch } = window.wp.data; - const isCurrentlyFixed = - select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ); - if ( isCurrentlyFixed !== _isFixed ) { - dispatch( 'core/edit-post' ).toggleFeature( 'fixedToolbar' ); - } + window.wp.data + .dispatch( 'core/preferences' ) + .set( 'core/edit-post', 'fixedToolbar', _isFixed ); }, isFixed ); } From 487f230ff9bd3132546fb19b1096cd68ffb10207 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Tue, 20 Jun 2023 16:05:40 +1200 Subject: [PATCH 108/163] Reusable blocks: Rename to 'Patterns' and add option to also add a non-synced Pattern (#51144) --------- Co-authored-by: Saxon Fletcher Co-authored-by: Daniel Richards --- docs/reference-guides/core-blocks.md | 4 +- .../data/data-core-block-editor.md | 1 + lib/compat/wordpress-6.3/blocks.php | 110 ++++++++++++++++++ .../components/inserter/block-patterns-tab.js | 59 +++++++++- .../inserter/hooks/use-block-types-state.js | 7 +- .../inserter/reusable-blocks-tab.js | 6 +- .../src/components/inserter/tabs.js | 6 +- packages/block-editor/src/store/selectors.js | 13 ++- .../block-editor/src/store/test/selectors.js | 34 +++--- packages/block-library/src/block/block.json | 2 +- packages/block-library/src/block/edit.js | 1 + .../__snapshots__/transforms.native.js.snap | 4 +- .../src/block/test/edit.native.js | 6 +- .../src/block/test/transforms.native.js | 2 +- packages/block-library/src/pattern/block.json | 2 +- .../src/create-reusable-block.js | 12 +- packages/e2e-test-utils/src/inserter.js | 16 +-- .../block-editor-keyboard-shortcuts.test.js | 2 +- .../editor/various/reusable-blocks.test.js | 24 ++-- .../components/sidebar/post-status/index.js | 3 +- packages/edit-post/src/plugins/index.js | 2 +- .../index.js | 2 +- packages/editor/src/components/index.js | 1 + .../src/components/post-sync-status/index.js | 55 +++++++++ .../components/post-sync-status/style.scss | 16 +++ packages/editor/src/style.scss | 1 + .../reusable-block-convert-button.js | 54 +++++++-- .../reusable-blocks-manage-button.js | 2 +- packages/reusable-blocks/src/store/actions.js | 23 +++- 29 files changed, 389 insertions(+), 81 deletions(-) create mode 100644 packages/editor/src/components/post-sync-status/index.js create mode 100644 packages/editor/src/components/post-sync-status/style.scss diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index dec943d4bb7905..0a6519576da3a3 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -36,7 +36,7 @@ Add a user’s avatar. ([Source](https://github.com/WordPress/gutenberg/tree/tru - **Supports:** align, color (~~background~~, ~~text~~), spacing (margin, padding), ~~alignWide~~, ~~html~~ - **Attributes:** isLink, linkTarget, size, userId -## Reusable block +## Pattern Create and save content to reuse across your site. Update the block, and the changes apply everywhere it’s used. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/block)) @@ -473,7 +473,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~ - **Attributes:** align, content, direction, dropCap, placeholder -## Pattern +## Pattern placeholder Show a block pattern. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/pattern)) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 8b0f24e60fb7bf..20d6ebfe5291aa 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -538,6 +538,7 @@ _Parameters_ - _state_ `Object`: Editor state. - _rootClientId_ `?string`: Optional root client ID of block list. +- _syncStatus_ `?string`: Optional sync status to filter pattern blocks by. _Returns_ diff --git a/lib/compat/wordpress-6.3/blocks.php b/lib/compat/wordpress-6.3/blocks.php index 5f017997f52b58..6902a258c5c3d8 100644 --- a/lib/compat/wordpress-6.3/blocks.php +++ b/lib/compat/wordpress-6.3/blocks.php @@ -26,3 +26,113 @@ function gutenberg_add_selectors_property_to_block_type_settings( $settings, $me return $settings; } add_filter( 'block_type_metadata_settings', 'gutenberg_add_selectors_property_to_block_type_settings', 10, 2 ); + +/** + * Renames Reusable block CPT to Pattern. + * + * Note: This should be removed when the minimum required WP version is >= 6.3. + * + * @see https://github.com/WordPress/gutenberg/pull/51144 + * + * @param array $args Register post type args. + * @param string $post_type The post type string. + * + * @return array Register post type args. + */ +function gutenberg_rename_reusable_block_cpt_to_pattern( $args, $post_type ) { + if ( 'wp_block' === $post_type ) { + $args['labels']['name'] = _x( 'Patterns', 'post type general name' ); + $args['labels']['singular_name'] = _x( 'Pattern', 'post type singular name' ); + $args['labels']['add_new_item'] = __( 'Add new Pattern' ); + $args['labels']['new_item'] = __( 'New Pattern' ); + $args['labels']['edit_item'] = __( 'Edit Pattern' ); + $args['labels']['view_item'] = __( 'View Pattern' ); + $args['labels']['all_items'] = __( 'All Patterns' ); + $args['labels']['search_items'] = __( 'Search Patterns' ); + $args['labels']['not_found'] = __( 'No Patterns found.' ); + $args['labels']['not_found_in_trash'] = __( 'No Patterns found in Trash.' ); + $args['labels']['filter_items_list'] = __( 'Filter Patterns list' ); + $args['labels']['items_list_navigation'] = __( 'Patterns list navigation' ); + $args['labels']['items_list'] = __( 'Patterns list' ); + $args['labels']['item_published'] = __( 'Pattern published.' ); + $args['labels']['item_published_privately'] = __( 'Pattern published privately.' ); + $args['labels']['item_reverted_to_draft'] = __( 'Pattern reverted to draft.' ); + $args['labels']['item_scheduled'] = __( 'Pattern scheduled.' ); + $args['labels']['item_updated'] = __( 'Pattern updated.' ); + } + + return $args; +} + +add_filter( 'register_post_type_args', 'gutenberg_rename_reusable_block_cpt_to_pattern', 10, 2 ); + +/** + * Adds custom fields support to the wp_block post type so an unsynced option can be added. + * + * Note: This should be removed when the minimum required WP version is >= 6.3. + * + * @see https://github.com/WordPress/gutenberg/pull/51144 + * + * @param array $args Register post type args. + * @param string $post_type The post type string. + * + * @return array Register post type args. + */ +function gutenberg_add_custom_fields_to_wp_block( $args, $post_type ) { + if ( 'wp_block' === $post_type ) { + array_push( $args['supports'], 'custom-fields' ); + } + + return $args; +} +add_filter( 'register_post_type_args', 'gutenberg_add_custom_fields_to_wp_block', 10, 2 ); + +/** + * Adds sync_status meta fields to the wp_block post type so an unsynced option can be added. + * + * Note: This should be removed when the minimum required WP version is >= 6.3. + * + * @see https://github.com/WordPress/gutenberg/pull/51144 + * + * @return void + */ +function gutenberg_wp_block_register_post_meta() { + $post_type = 'wp_block'; + register_post_meta( + $post_type, + 'sync_status', + array( + 'auth_callback' => function() { + return current_user_can( 'edit_posts' ); + }, + 'sanitize_callback' => 'gutenberg_wp_block_sanitize_post_meta', + 'single' => true, + 'type' => 'string', + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'string', + 'properties' => array( + 'sync_status' => array( + 'type' => 'string', + ), + ), + ), + ), + ) + ); +} +/** + * Sanitizes the array of wp_block post meta sync_status string. + * + * Note: This should be removed when the minimum required WP version is >= 6.3. + * + * @see https://github.com/WordPress/gutenberg/pull/51144 + * + * @param array $meta_value String to sanitize. + * + * @return array Sanitized string. + */ +function gutenberg_wp_block_sanitize_post_meta( $meta_value ) { + return sanitize_text_field( $meta_value ); +} +add_action( 'init', 'gutenberg_wp_block_register_post_meta' ); diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab.js b/packages/block-editor/src/components/inserter/block-patterns-tab.js index 49a5939107bb1f..578791e8802692 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab.js @@ -18,6 +18,7 @@ import { Button, } from '@wordpress/components'; import { Icon, chevronRight, chevronLeft } from '@wordpress/icons'; +import { parse } from '@wordpress/blocks'; import { focus } from '@wordpress/dom'; /** @@ -27,6 +28,7 @@ import usePatternsState from './hooks/use-patterns-state'; import BlockPatternList from '../block-patterns-list'; import PatternsExplorerModal from './block-patterns-explorer/explorer'; import MobileTabNavigation from './mobile-tab-navigation'; +import useBlockTypesState from './hooks/use-block-types-state'; const noop = () => {}; @@ -49,6 +51,18 @@ function usePatternsCategories( rootClientId ) { rootClientId ); + const [ unsyncedPatterns ] = useBlockTypesState( + rootClientId, + undefined, + 'unsynced' + ); + + const filteredUnsyncedPatterns = useMemo( () => { + return unsyncedPatterns.filter( + ( { category: unsyncedPatternCategory } ) => + unsyncedPatternCategory === 'reusable' + ); + }, [ unsyncedPatterns ] ); const hasRegisteredCategory = useCallback( ( pattern ) => { if ( ! pattern.categories || ! pattern.categories.length ) { @@ -93,9 +107,20 @@ function usePatternsCategories( rootClientId ) { label: _x( 'Uncategorized' ), } ); } + if ( filteredUnsyncedPatterns.length > 0 ) { + categories.push( { + name: 'reusable', + label: _x( 'Custom patterns' ), + } ); + } return categories; - }, [ allPatterns, allCategories ] ); + }, [ + allCategories, + allPatterns, + filteredUnsyncedPatterns.length, + hasRegisteredCategory, + ] ); return populatedCategories; } @@ -144,6 +169,24 @@ export function BlockPatternsCategoryPanel( { onInsert, rootClientId ); + const [ unsyncedPatterns ] = useBlockTypesState( + rootClientId, + onInsert, + 'unsynced' + ); + const filteredUnsyncedPatterns = useMemo( () => { + return unsyncedPatterns + .filter( + ( { category: unsyncedPatternCategory } ) => + unsyncedPatternCategory === 'reusable' + ) + .map( ( syncedPattern ) => ( { + ...syncedPattern, + blocks: parse( syncedPattern.content, { + __unstableSkipMigrationLogs: true, + } ), + } ) ); + }, [ unsyncedPatterns ] ); const availableCategories = usePatternsCategories( rootClientId ); const currentCategoryPatterns = useMemo( @@ -167,13 +210,19 @@ export function BlockPatternsCategoryPanel( { } ), [ allPatterns, category ] ); - - const currentShownPatterns = useAsyncList( currentCategoryPatterns ); + const patterns = + category.name === 'reusable' + ? filteredUnsyncedPatterns + : currentCategoryPatterns; + const currentShownPatterns = useAsyncList( patterns ); // Hide block pattern preview on unmount. useEffect( () => () => onHover( null ), [] ); - if ( ! currentCategoryPatterns.length ) { + if ( + ! currentCategoryPatterns.length && + ! filteredUnsyncedPatterns.length + ) { return null; } @@ -185,7 +234,7 @@ export function BlockPatternsCategoryPanel( {

    { category.description }

    { +const useBlockTypesState = ( rootClientId, onInsert, syncStatus ) => { const { categories, collections, items } = useSelect( ( select ) => { const { getInserterItems } = select( blockEditorStore ); @@ -30,10 +31,10 @@ const useBlockTypesState = ( rootClientId, onInsert ) => { return { categories: getCategories(), collections: getCollections(), - items: getInserterItems( rootClientId ), + items: getInserterItems( rootClientId, syncStatus ), }; }, - [ rootClientId ] + [ rootClientId, syncStatus ] ); const onSelectItem = useCallback( diff --git a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js index 9505dd77f3b94d..65930fa9fcd4a6 100644 --- a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js +++ b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js @@ -29,12 +29,12 @@ function ReusableBlocksList( { onHover, onInsert, rootClientId } ) { } return ( - + ); @@ -67,7 +67,7 @@ export function ReusableBlocksTab( { rootClientId, onInsert, onHover } ) { post_type: 'wp_block', } ) } > - { __( 'Manage Reusable blocks' ) } + { __( 'Manage custom patterns' ) }
    diff --git a/packages/block-editor/src/components/inserter/tabs.js b/packages/block-editor/src/components/inserter/tabs.js index 6f8377892059b5..1ff8b529707a4b 100644 --- a/packages/block-editor/src/components/inserter/tabs.js +++ b/packages/block-editor/src/components/inserter/tabs.js @@ -13,13 +13,13 @@ const blocksTab = { }; const patternsTab = { name: 'patterns', - /* translators: Patterns tab title in the block inserter. */ + /* translators: Theme and Directory Patterns tab title in the block inserter. */ title: __( 'Patterns' ), }; const reusableBlocksTab = { name: 'reusable', - /* translators: Reusable blocks tab title in the block inserter. */ - title: __( 'Reusable' ), + /* translators: Locally created Patterns tab title in the block inserter. */ + title: __( 'Synced patterns' ), icon: reusableBlockIcon, }; const mediaTab = { diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 1616a2fde8b1b5..6a62f50c2a2f7d 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1945,6 +1945,7 @@ const buildBlockTypeItem = * * @param {Object} state Editor state. * @param {?string} rootClientId Optional root client ID of block list. + * @param {?string} syncStatus Optional sync status to filter pattern blocks by. * * @return {WPEditorInserterItem[]} Items that appear in inserter. * @@ -1961,7 +1962,7 @@ const buildBlockTypeItem = * @property {number} frecency Heuristic that combines frequency and recency. */ export const getInserterItems = createSelector( - ( state, rootClientId = null ) => { + ( state, rootClientId = null, syncStatus ) => { const buildBlockTypeInserterItem = buildBlockTypeItem( state, { buildScope: 'inserter', } ); @@ -2026,6 +2027,7 @@ export const getInserterItems = createSelector( isDisabled: false, utility: 1, // Deprecated. frecency, + content: reusableBlock.content.raw, }; }; @@ -2040,7 +2042,14 @@ export const getInserterItems = createSelector( 'core/block', rootClientId ) - ? getReusableBlocks( state ).map( buildReusableBlockInserterItem ) + ? getReusableBlocks( state ) + .filter( + ( reusableBlock ) => + syncStatus === reusableBlock.meta?.sync_status || + ( ! syncStatus && + reusableBlock.meta?.sync_status === '' ) + ) + .map( buildReusableBlockInserterItem ) : []; const items = blockTypeInserterItems.reduce( ( accumulator, item ) => { diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index dca9b847bc5a2f..270bceaad447fb 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -3326,36 +3326,36 @@ describe( 'selectors', () => { ( item ) => item.id === 'core/test-block-a' ); expect( testBlockAItem ).toEqual( { + category: 'design', + description: undefined, + example: undefined, + frecency: 0, + icon: { src: 'test' }, id: 'core/test-block-a', - name: 'core/test-block-a', initialAttributes: {}, - title: 'Test Block A', - icon: { - src: 'test', - }, - category: 'design', - keywords: [ 'testing' ], - variations: [], isDisabled: false, + keywords: [ 'testing' ], + name: 'core/test-block-a', + title: 'Test Block A', utility: 1, - frecency: 0, + variations: [], } ); const reusableBlockItem = items.find( ( item ) => item.id === 'core/block/1' ); expect( reusableBlockItem ).toEqual( { + category: 'reusable', + content: '', + frecency: 0, + icon: { src: 'test' }, id: 'core/block/1', - name: 'core/block', initialAttributes: { ref: 1 }, - title: 'Reusable Block 1', - icon: { - src: 'test', - }, - category: 'reusable', - keywords: [], isDisabled: false, + keywords: [], + name: 'core/block', + syncStatus: undefined, + title: 'Reusable Block 1', utility: 1, - frecency: 0, } ); } ); diff --git a/packages/block-library/src/block/block.json b/packages/block-library/src/block/block.json index 78fed058a5bd13..5846e7ead0c9b6 100644 --- a/packages/block-library/src/block/block.json +++ b/packages/block-library/src/block/block.json @@ -2,7 +2,7 @@ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "core/block", - "title": "Reusable block", + "title": "Pattern", "category": "reusable", "description": "Create and save content to reuse across your site. Update the block, and the changes apply everywhere it’s used.", "textdomain": "default", diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index fcba45450ea5e9..cc1ec16aeedc24 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -59,6 +59,7 @@ export default function ReusableBlockEdit( { attributes: { ref }, clientId } ) { 'wp_block', { id: ref } ); + const [ title, setTitle ] = useEntityProp( 'postType', 'wp_block', diff --git a/packages/block-library/src/block/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/block/test/__snapshots__/transforms.native.js.snap index 7489c0b04954a7..3c4d791eb9f755 100644 --- a/packages/block-library/src/block/test/__snapshots__/transforms.native.js.snap +++ b/packages/block-library/src/block/test/__snapshots__/transforms.native.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Reusable block block transforms to Columns block 1`] = ` +exports[`Pattern block transforms to Columns block 1`] = ` "
    @@ -8,7 +8,7 @@ exports[`Reusable block block transforms to Columns block 1`] = ` " `; -exports[`Reusable block block transforms to Group block 1`] = ` +exports[`Pattern block transforms to Group block 1`] = ` "
    " diff --git a/packages/block-library/src/block/test/edit.native.js b/packages/block-library/src/block/test/edit.native.js index 4652f8ba20f385..ae9d3d7c03e3e5 100644 --- a/packages/block-library/src/block/test/edit.native.js +++ b/packages/block-library/src/block/test/edit.native.js @@ -114,7 +114,7 @@ describe( 'Reusable block', () => { // Get the reusable block. const [ reusableBlock ] = await screen.findAllByLabelText( - /Reusable block Block\. Row 1/ + /Pattern Block\. Row 1/ ); expect( reusableBlock ).toBeDefined(); @@ -131,7 +131,7 @@ describe( 'Reusable block', () => { } ); const [ reusableBlock ] = await screen.findAllByLabelText( - /Reusable block Block\. Row 1/ + /Pattern Block\. Row 1/ ); const blockDeleted = within( reusableBlock ).getByText( @@ -164,7 +164,7 @@ describe( 'Reusable block', () => { } ); const [ reusableBlock ] = await screen.findByLabelText( - /Reusable block Block\. Row 1/ + /Pattern Block\. Row 1/ ); const innerBlockListWrapper = await within( diff --git a/packages/block-library/src/block/test/transforms.native.js b/packages/block-library/src/block/test/transforms.native.js index 9771b40743a422..95104ac6133993 100644 --- a/packages/block-library/src/block/test/transforms.native.js +++ b/packages/block-library/src/block/test/transforms.native.js @@ -9,7 +9,7 @@ import { getBlockTransformOptions, } from 'test/helpers'; -const block = 'Reusable block'; +const block = 'Pattern'; const initialHtml = ` `; diff --git a/packages/block-library/src/pattern/block.json b/packages/block-library/src/pattern/block.json index 16428e2969ca2f..1fc319b8fb33d2 100644 --- a/packages/block-library/src/pattern/block.json +++ b/packages/block-library/src/pattern/block.json @@ -2,7 +2,7 @@ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "core/pattern", - "title": "Pattern", + "title": "Pattern placeholder", "category": "theme", "description": "Show a block pattern.", "supports": { diff --git a/packages/e2e-test-utils/src/create-reusable-block.js b/packages/e2e-test-utils/src/create-reusable-block.js index ec35e073908477..266e0525d34bd2 100644 --- a/packages/e2e-test-utils/src/create-reusable-block.js +++ b/packages/e2e-test-utils/src/create-reusable-block.js @@ -15,22 +15,30 @@ import { canvas } from './canvas'; export const createReusableBlock = async ( content, title ) => { const reusableBlockNameInputSelector = '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; + const syncToggleSelector = + '.reusable-blocks-menu-items__convert-modal .components-form-toggle__input'; + const syncToggleSelectorChecked = + '.reusable-blocks-menu-items__convert-modal .components-form-toggle.is-checked'; // Insert a paragraph block await insertBlock( 'Paragraph' ); await page.keyboard.type( content ); await clickBlockToolbarButton( 'Options' ); - await clickMenuItem( 'Create Reusable block' ); + await clickMenuItem( 'Create a Pattern' ); const nameInput = await page.waitForSelector( reusableBlockNameInputSelector ); await nameInput.click(); await page.keyboard.type( title ); + + const syncToggle = await page.waitForSelector( syncToggleSelector ); + syncToggle.click(); + await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); // Wait for creation to finish await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block created."]' + '//*[contains(@class, "components-snackbar")]/*[text()="Synced Pattern created."]' ); // Check that we have a reusable block on the page diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js index 58f6d01a66514d..cdd1c534d928db 100644 --- a/packages/e2e-test-utils/src/inserter.js +++ b/packages/e2e-test-utils/src/inserter.js @@ -106,8 +106,8 @@ export async function selectGlobalInserterTab( label ) { case 'Media': labelSelector = `. = "${ label }"`; break; - case 'Reusable': - // Reusable tab label is an icon, hence the different selector. + case 'Synced patterns': + // Synced patterns tab label is an icon, hence the different selector. labelSelector = `@aria-label = "${ label }"`; break; } @@ -180,7 +180,7 @@ export async function searchGlobalInserter( category, searchTerm ) { switch ( category ) { case 'Blocks': case 'Patterns': - case 'Reusable': { + case 'Synced patterns': { waitForInsertElement = async () => { return await page.waitForXPath( `//*[@role='option' and contains(., '${ searchTerm }')]` @@ -220,7 +220,7 @@ export async function searchGlobalInserter( category, searchTerm ) { * If the entity is not instantly available in the open inserter, a search will * be performed. If the search returns no results, an error will be thrown. * - * Available categories: Blocks, Patterns, Reusable and Block Directory. + * Available categories: Blocks, Patterns, Synced patterns and Block Directory. * * @param {string} category The category to insert from. * @param {string} searchTerm The term by which to find the entity to insert. @@ -231,8 +231,8 @@ export async function insertFromGlobalInserter( category, searchTerm ) { let insertButton; - if ( [ 'Blocks', 'Reusable' ].includes( category ) ) { - // If it's a block, see it it's insertable without searching... + if ( [ 'Blocks', 'Synced patterns' ].includes( category ) ) { + // If it's a block, see if it's insertable without searching... try { insertButton = ( await page.$x( @@ -260,7 +260,7 @@ export async function insertFromGlobalInserter( category, searchTerm ) { await insertButton.click(); // Extra wait for the reusable block to be ready. - if ( category === 'Reusable' ) { + if ( category === 'Synced patterns' ) { await canvas().waitForSelector( '.block-library-block__reusable-block-container' ); @@ -347,7 +347,7 @@ export async function insertPattern( searchTerm ) { * insert. */ export async function insertReusableBlock( searchTerm ) { - await insertFromGlobalInserter( 'Reusable', searchTerm ); + await insertFromGlobalInserter( 'Synced patterns', searchTerm ); } /** diff --git a/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js b/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js index f1e6be7b816abb..97248c472e4ac4 100644 --- a/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js +++ b/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js @@ -90,7 +90,7 @@ describe( 'block editor keyboard shortcuts', () => { } ); it( 'should prevent deleting multiple selected blocks from inputs', async () => { await clickBlockToolbarButton( 'Options' ); - await clickMenuItem( 'Create Reusable block' ); + await clickMenuItem( 'Create a Pattern' ); const reusableBlockNameInputSelector = '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; const nameInput = await page.waitForSelector( diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js index 3215e4185c08fa..416782733dfaa2 100644 --- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js @@ -23,6 +23,10 @@ const reusableBlockNameInputSelector = '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; const reusableBlockInspectorNameInputSelector = '.block-editor-block-inspector .components-text-control__input'; +const syncToggleSelector = + '.reusable-blocks-menu-items__convert-modal .components-form-toggle__input'; +const syncToggleSelectorChecked = + '.reusable-blocks-menu-items__convert-modal .components-form-toggle.is-checked'; const saveAll = async () => { const publishButtonSelector = @@ -193,7 +197,7 @@ describe( 'Reusable blocks', () => { // Convert block to a reusable block. await clickBlockToolbarButton( 'Options' ); - await clickMenuItem( 'Create Reusable block' ); + await clickMenuItem( 'Create a Pattern' ); // Set title. const nameInput = await page.waitForSelector( @@ -201,11 +205,14 @@ describe( 'Reusable blocks', () => { ); await nameInput.click(); await page.keyboard.type( 'Multi-selection reusable block' ); + const syncToggle = await page.waitForSelector( syncToggleSelector ); + syncToggle.click(); + await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); // Wait for creation to finish. await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block created."]' + '//*[contains(@class, "components-snackbar")]/*[text()="Synced Pattern created."]' ); await clearAllBlocks(); @@ -259,7 +266,7 @@ describe( 'Reusable blocks', () => { // Save the reusable block. await page.click( publishButtonSelector ); await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block updated."]' + '//*[contains(@class, "components-snackbar")]/*[text()="Pattern updated."]' ); await createNewPost(); @@ -340,12 +347,12 @@ describe( 'Reusable blocks', () => { await canvas().click( 'p[aria-label="Paragraph block"]' ); await page.keyboard.type( '2' ); const selector = - '//div[@aria-label="Block: Reusable block"]//p[@aria-label="Paragraph block"][.="12"]'; + '//div[@aria-label="Block: Pattern"]//p[@aria-label="Paragraph block"][.="12"]'; const reusableBlockWithParagraph = await page.$x( selector ); expect( reusableBlockWithParagraph ).toBeTruthy(); // Convert back to regular blocks. - await clickBlockToolbarButton( 'Select Reusable block' ); + await clickBlockToolbarButton( 'Select Pattern' ); await clickBlockToolbarButton( 'Convert to regular block' ); await page.waitForXPath( selector, { hidden: true, @@ -376,15 +383,18 @@ describe( 'Reusable blocks', () => { // Convert to reusable. await clickBlockToolbarButton( 'Options' ); - await clickMenuItem( 'Create Reusable block' ); + await clickMenuItem( 'Create a Pattern' ); const nameInput = await page.waitForSelector( reusableBlockNameInputSelector ); await nameInput.click(); await page.keyboard.type( 'Block with styles' ); + const syncToggle = await page.waitForSelector( syncToggleSelector ); + syncToggle.click(); + await page.waitForSelector( syncToggleSelectorChecked ); await page.keyboard.press( 'Enter' ); const reusableBlock = await canvas().waitForSelector( - '.block-editor-block-list__block[aria-label="Block: Reusable block"]' + '.block-editor-block-list__block[aria-label="Block: Pattern"]' ); expect( reusableBlock ).toBeTruthy(); } ); diff --git a/packages/edit-post/src/components/sidebar/post-status/index.js b/packages/edit-post/src/components/sidebar/post-status/index.js index 8304bb8b4f6ea4..5cc9b70bf9ac31 100644 --- a/packages/edit-post/src/components/sidebar/post-status/index.js +++ b/packages/edit-post/src/components/sidebar/post-status/index.js @@ -8,7 +8,7 @@ import { } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose, ifCondition } from '@wordpress/compose'; -import { PostSwitchToDraftButton } from '@wordpress/editor'; +import { PostSwitchToDraftButton, PostSyncStatus } from '@wordpress/editor'; /** * Internal dependencies @@ -51,6 +51,7 @@ function PostStatus( { isOpened, onTogglePanel } ) { + { fills } - { __( 'Manage Reusable blocks' ) } + { __( 'Manage Patterns' ) } { + const { getEditedPostAttribute } = select( editorStore ); + return { + meta: getEditedPostAttribute( 'meta' ), + postType: getEditedPostAttribute( 'type' ), + }; + }, [] ); + if ( postType !== 'wp_block' ) { + return null; + } + const onUpdateSync = ( syncStatus ) => + editPost( { + meta: { + ...meta, + wp_block: + syncStatus === 'unsynced' + ? { sync_status: syncStatus } + : null, + }, + } ); + const syncStatus = meta?.wp_block?.sync_status; + const isFullySynced = ! syncStatus; + + return ( + + { __( 'Syncing' ) } + { + onUpdateSync( + syncStatus === 'unsynced' ? 'fully' : 'unsynced' + ); + } } + /> + + ); +} diff --git a/packages/editor/src/components/post-sync-status/style.scss b/packages/editor/src/components/post-sync-status/style.scss new file mode 100644 index 00000000000000..385577b3334d86 --- /dev/null +++ b/packages/editor/src/components/post-sync-status/style.scss @@ -0,0 +1,16 @@ +.edit-post-sync-status { + width: 100%; + position: relative; + justify-content: flex-start; + + > span { + display: block; + width: 45%; + flex-shrink: 0; + } + + .components-base-control { + // Match padding on tertiary buttons for alignment. + padding-left: $grid-unit-15 * 0.5; + } +} diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index 9d035ec4d654aa..dbffbbef4d5212 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -11,6 +11,7 @@ @import "./components/post-publish-button/style.scss"; @import "./components/post-publish-panel/style.scss"; @import "./components/post-saved-state/style.scss"; +@import "./components/post-sync-status/style.scss"; @import "./components/post-taxonomies/style.scss"; @import "./components/post-text-editor/style.scss"; @import "./components/post-url/style.scss"; diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js index 18824b892544a8..c8203fdd73c409 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js @@ -14,6 +14,7 @@ import { TextControl, __experimentalHStack as HStack, __experimentalVStack as VStack, + ToggleControl, } from '@wordpress/components'; import { symbol } from '@wordpress/icons'; import { useDispatch, useSelect } from '@wordpress/data'; @@ -38,6 +39,7 @@ export default function ReusableBlockConvertButton( { clientIds, rootClientId, } ) { + const [ syncType, setSyncType ] = useState( 'unsynced' ); const [ isModalOpen, setIsModalOpen ] = useState( false ); const [ title, setTitle ] = useState( '' ); const canConvert = useSelect( @@ -77,7 +79,7 @@ export default function ReusableBlockConvertButton( { return _canConvert; }, - [ clientIds ] + [ clientIds, rootClientId ] ); const { __experimentalConvertBlocksToReusable: convertBlocksToReusable } = @@ -88,17 +90,32 @@ export default function ReusableBlockConvertButton( { const onConvert = useCallback( async function ( reusableBlockTitle ) { try { - await convertBlocksToReusable( clientIds, reusableBlockTitle ); - createSuccessNotice( __( 'Reusable block created.' ), { - type: 'snackbar', - } ); + await convertBlocksToReusable( + clientIds, + reusableBlockTitle, + syncType + ); + createSuccessNotice( + syncType === 'fully' + ? __( 'Synced Pattern created.' ) + : __( 'Unsynced Pattern created.' ), + { + type: 'snackbar', + } + ); } catch ( error ) { createErrorNotice( error.message, { type: 'snackbar', } ); } }, - [ clientIds ] + [ + convertBlocksToReusable, + clientIds, + syncType, + createSuccessNotice, + createErrorNotice, + ] ); if ( ! canConvert ) { @@ -111,15 +128,13 @@ export default function ReusableBlockConvertButton( { <> { - setIsModalOpen( true ); - } } + onClick={ () => setIsModalOpen( true ) } > - { __( 'Create Reusable block' ) } + { __( 'Create a Pattern' ) } { isModalOpen && ( { setIsModalOpen( false ); setTitle( '' ); @@ -142,6 +157,23 @@ export default function ReusableBlockConvertButton( { value={ title } onChange={ setTitle } /> + + { + setSyncType( + syncType === 'fully' + ? 'unsynced' + : 'fully' + ); + } } + /> + ) } + renderContent={ ( { onClose } ) => ( + <> + +
    + + + { status !== 'private' && ( + + + { showPassword && ( + + saveStatus( { + password: value, + } ) + } + value={ password } + /* eslint-disable jsx-a11y/no-autofocus */ + autoFocus={ ! password } + /* eslint-enable jsx-a11y/no-autofocus */ + placeholder={ __( + 'Enter a secure password' + ) } + type="password" + /> + ) } + + ) } + +
    + + ) } + /> +
    + ); +} diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-summary.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-summary.js new file mode 100644 index 00000000000000..3dce743b298d45 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-summary.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +import { __experimentalVStack as VStack } from '@wordpress/components'; +/** + * Internal dependencies + */ +import PageStatus from './page-status'; +import PublishDate from './publish-date'; + +export default function PageSummary( { + status, + date, + password, + postId, + postType, +} ) { + return ( + + + + + ); +} diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/publish-date.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/publish-date.js new file mode 100644 index 00000000000000..d000394f6816ba --- /dev/null +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/publish-date.js @@ -0,0 +1,94 @@ +/** + * WordPress dependencies + */ +import { + Button, + Dropdown, + __experimentalText as Text, + __experimentalHStack as HStack, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; +import { useState, useMemo } from '@wordpress/element'; +import { store as coreStore } from '@wordpress/core-data'; +import { store as noticesStore } from '@wordpress/notices'; +import { __experimentalPublishDateTimePicker as PublishDateTimePicker } from '@wordpress/block-editor'; +import { humanTimeDiff } from '@wordpress/date'; + +export default function ChangeStatus( { postType, postId, status, date } ) { + const { editEntityRecord } = useDispatch( coreStore ); + const { createErrorNotice } = useDispatch( noticesStore ); + + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + // Memoize popoverProps to avoid returning a new object every time. + const popoverProps = useMemo( + () => ( { + // Anchor the popover to the middle of the entire row so that it doesn't + // move around when the label changes. + anchor: popoverAnchor, + 'aria-label': __( 'Change publish date' ), + placement: 'bottom-end', + } ), + [ popoverAnchor ] + ); + + const saveDate = async ( newDate ) => { + try { + let newStatus = status; + if ( status === 'future' && new Date( newDate ) < new Date() ) { + newStatus = 'publish'; + } else if ( + status === 'publish' && + new Date( newDate ) > new Date() + ) { + newStatus = 'future'; + } + await editEntityRecord( 'postType', postType, postId, { + status: newStatus, + date: newDate, + } ); + } catch ( error ) { + const errorMessage = + error.message && error.code !== 'unknown_error' + ? error.message + : __( 'An error occurred while updating the status' ); + + createErrorNotice( errorMessage, { + type: 'snackbar', + } ); + } + }; + + const relateToNow = date ? humanTimeDiff( date ) : __( 'Immediately' ); + + return ( + + + { __( 'Publish' ) } + + ( + + ) } + renderContent={ ( { onClose } ) => ( + + ) } + /> + + ); +} diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss index 58178303e48e5b..0a618d9ff53d3f 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss +++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/style.scss @@ -8,3 +8,34 @@ .edit-site-page-panels__edit-template-button { justify-content: center; } + +.edit-site-change-status__content { + .components-popover__content { + min-width: 320px; + padding: $grid-unit-20; + } + .edit-site-change-status__options { + .components-base-control__field > .components-v-stack { + gap: $grid-unit-10; + } + label { + .components-text { + display: block; + margin-left: $radio-input-size + 6; + } + } + } +} + +.edit-site-summary-field { + .components-dropdown { + flex-grow: 1; + } + .edit-site-summary-field__trigger { + width: 100%; + } + .edit-site-summary-field__label { + width: 30%; + } +} + diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js index 0b8b459e482e6f..289bb25e9a071d 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js @@ -36,6 +36,7 @@ function getPageDetails( page ) { ), }, diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js index 208d8adc59ab93..13aba13aacbecb 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { dateI18n, getDate, getSettings, humanTimeDiff } from '@wordpress/date'; +import { dateI18n, getDate, humanTimeDiff } from '@wordpress/date'; import { createInterpolateElement } from '@wordpress/element'; import { Path, SVG } from '@wordpress/primitives'; @@ -41,35 +41,39 @@ const pendingIcon = ( ); -export default function StatusLabel( { status, date } ) { +export default function StatusLabel( { status, date, short } ) { const relateToNow = humanTimeDiff( date ); let statusLabel = ''; let statusIcon = pendingIcon; switch ( status ) { case 'publish': - statusLabel = createInterpolateElement( - sprintf( - /* translators: %s: is the relative time when the post was published. */ - __( 'Published ' ), - relateToNow - ), - { time:
    diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js index 4b3a37a52f9423..08c17304ca38b5 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js @@ -56,9 +56,9 @@ export default function ScreenNavigationMoreMenu( props ) { > { __( 'Duplicate' ) } + + { openDeleteModal(); From 0c9301f56f4a768627bd38b04b3f33c368fd4438 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Tue, 20 Jun 2023 12:45:58 +0100 Subject: [PATCH 126/163] Move the go to action to the leaf more menu (#50843) --- .../navigation-menu-editor.js | 46 +------------- .../leaf-more-menu.js | 60 +++++++++++++++++++ .../navigation-menu-content.js | 6 +- 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/navigation-menu-editor.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/navigation-menu-editor.js index 640c8751a51ac1..7d3be6f631f438 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/navigation-menu-editor.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/navigation-menu-editor.js @@ -1,11 +1,10 @@ /** * WordPress dependencies */ -import { useCallback, useMemo } from '@wordpress/element'; +import { useMemo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { BlockEditorProvider } from '@wordpress/block-editor'; import { createBlock } from '@wordpress/blocks'; -import { privateApis as routerPrivateApis } from '@wordpress/router'; /** * Internal dependencies @@ -13,48 +12,10 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; import { unlock } from '../../lock-unlock'; import { store as editSiteStore } from '../../store'; import NavigationMenuContent from '../sidebar-navigation-screen-navigation-menus/navigation-menu-content'; -import { - isPreviewingTheme, - currentlyPreviewingTheme, -} from '../../utils/is-previewing-theme'; - -const { useHistory } = unlock( routerPrivateApis ); const noop = () => {}; export default function NavigationMenuEditor( { navigationMenuId } ) { - const history = useHistory(); - - const onSelect = useCallback( - ( selectedBlock ) => { - const { attributes, name } = selectedBlock; - if ( - attributes.kind === 'post-type' && - attributes.id && - attributes.type && - history - ) { - history.push( { - postType: attributes.type, - postId: attributes.id, - ...( isPreviewingTheme() && { - gutenberg_theme_preview: currentlyPreviewingTheme(), - } ), - } ); - } - if ( name === 'core/page-list-item' && attributes.id && history ) { - history.push( { - postType: 'page', - postId: attributes.id, - ...( isPreviewingTheme() && { - gutenberg_theme_preview: currentlyPreviewingTheme(), - } ), - } ); - } - }, - [ history ] - ); - const { storedSettings } = useSelect( ( select ) => { const { getSettings } = unlock( select( editSiteStore ) ); @@ -83,10 +44,7 @@ export default function NavigationMenuEditor( { navigationMenuId } ) { onInput={ noop } >
    - +
    ); diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js index 83921df8fae0b9..da89b3213632d8 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js @@ -5,15 +5,29 @@ import { chevronUp, chevronDown, moreVertical } from '@wordpress/icons'; import { DropdownMenu, MenuItem, MenuGroup } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; +import { useCallback } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { BlockTitle, store as blockEditorStore } from '@wordpress/block-editor'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; const POPOVER_PROPS = { className: 'block-editor-block-settings-menu__popover', placement: 'bottom-start', }; +/** + * Internal dependencies + */ +import { + isPreviewingTheme, + currentlyPreviewingTheme, +} from '../../utils/is-previewing-theme'; +import { unlock } from '../../lock-unlock'; + +const { useHistory } = unlock( routerPrivateApis ); + export default function LeafMoreMenu( props ) { + const history = useHistory(); const { block } = props; const { clientId } = block; const { moveBlocksDown, moveBlocksUp, removeBlocks } = @@ -25,6 +39,12 @@ export default function LeafMoreMenu( props ) { BlockTitle( { clientId, maximumLength: 25 } ) ); + const goToLabel = sprintf( + /* translators: %s: block name */ + __( 'Go to %s' ), + BlockTitle( { clientId, maximumLength: 25 } ) + ); + const rootClientId = useSelect( ( select ) => { const { getBlockRootClientId } = select( blockEditorStore ); @@ -34,6 +54,36 @@ export default function LeafMoreMenu( props ) { [ clientId ] ); + const onGoToPage = useCallback( + ( selectedBlock ) => { + const { attributes, name } = selectedBlock; + if ( + attributes.kind === 'post-type' && + attributes.id && + attributes.type && + history + ) { + history.push( { + postType: attributes.type, + postId: attributes.id, + ...( isPreviewingTheme() && { + gutenberg_theme_preview: currentlyPreviewingTheme(), + } ), + } ); + } + if ( name === 'core/page-list-item' && attributes.id && history ) { + history.push( { + postType: 'page', + postId: attributes.id, + ...( isPreviewingTheme() && { + gutenberg_theme_preview: currentlyPreviewingTheme(), + } ), + } ); + } + }, + [ history ] + ); + return ( { __( 'Move down' ) }
    + { block.attributes?.id && ( + { + onGoToPage(); + onClose(); + } } + > + { goToLabel } + + ) }
    { const { @@ -89,11 +89,9 @@ export default function NavigationMenuContent( { rootClientId, onSelect } ) { block.clientId, createBlock( 'core/navigation-link', block.attributes ) ); - } else { - onSelect( block ); } }, - [ onSelect, __unstableMarkNextChangeAsNotPersistent, replaceBlock ] + [ __unstableMarkNextChangeAsNotPersistent, replaceBlock ] ); // The hidden block is needed because it makes block edit side effects trigger. From 115e7b18157c8b6434aa4272d540955557336031 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Tue, 20 Jun 2023 14:08:21 +0200 Subject: [PATCH 127/163] Page details: Fix displaying slugs with non-latin characters (#51679) * Page details: Fix displaying slug with non-latin characters --- .../src/components/sidebar-navigation-screen-page/index.js | 5 ++++- .../sidebar-navigation-screen-page/page-details.js | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js index 8e9903be0fa5e2..c5799d97a80645 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/index.js @@ -14,6 +14,7 @@ import { decodeEntities } from '@wordpress/html-entities'; import { pencil } from '@wordpress/icons'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; import { escapeAttribute } from '@wordpress/escape-html'; +import { safeDecodeURIComponent, filterURLForDisplay } from '@wordpress/url'; /** * Internal dependencies @@ -90,7 +91,9 @@ export default function SidebarNavigationScreenPage() { className="edit-site-sidebar-navigation-screen__page-link" href={ record.link } > - { record.link.replace( /^(https?:\/\/)?/, '' ) } + { filterURLForDisplay( + safeDecodeURIComponent( record.link ) + ) } } content={ diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js index 289bb25e9a071d..a030460cd96404 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/page-details.js @@ -7,6 +7,7 @@ import { count as wordCount } from '@wordpress/wordcount'; import { useSelect } from '@wordpress/data'; import { decodeEntities } from '@wordpress/html-entities'; import { store as coreStore, useEntityRecord } from '@wordpress/core-data'; +import { safeDecodeURIComponent } from '@wordpress/url'; /** * Internal dependencies @@ -42,7 +43,11 @@ function getPageDetails( page ) { }, { label: __( 'Slug' ), - value: { page.slug }, + value: ( + + { safeDecodeURIComponent( page.slug ) } + + ), }, ]; From c783dc8890501a12ed9cb22f1cf15b132dc2e441 Mon Sep 17 00:00:00 2001 From: Sarah Norris Date: Tue, 20 Jun 2023 13:58:19 +0100 Subject: [PATCH 128/163] ItemGroup: Update button focus styles to be more consistent (#51576) * Update item-group focus styles * Update test snapshot * Update changelog * Update changelog entry --- packages/components/CHANGELOG.md | 1 + packages/components/src/item-group/styles.ts | 10 ++++++++++ .../preferences-modal/test/__snapshots__/index.js.snap | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 629513035d62ad..8320e389e62c60 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -5,6 +5,7 @@ ### Enhancements - `Modal`: Update corner radius to be between buttons and the site view frame, in a 2-4-8 system. ([#51254](https://github.com/WordPress/gutenberg/pull/51254)). +- `ItemGroup`: Update button focus state styles to be inline with other button focus states in the editor. ([#51576](https://github.com/WordPress/gutenberg/pull/51576)). ### Bug Fix diff --git a/packages/components/src/item-group/styles.ts b/packages/components/src/item-group/styles.ts index e90eac51c5244e..07ef6ba28db6e5 100644 --- a/packages/components/src/item-group/styles.ts +++ b/packages/components/src/item-group/styles.ts @@ -23,6 +23,16 @@ export const unstyledButton = css` &:hover { color: ${ COLORS.ui.theme }; } + + &:focus { + box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) + var( + --wp-components-color-accent, + var( --wp-admin-theme-color, ${ COLORS.ui.theme } ) + ); + // Windows high contrast mode. + outline: 2px solid transparent; + } `; export const itemWrapper = css` diff --git a/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap index a6fbe704c6d5bf..a962cfbe81919d 100644 --- a/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap +++ b/packages/edit-post/src/components/preferences-modal/test/__snapshots__/index.js.snap @@ -616,6 +616,14 @@ exports[`EditPostPreferencesModal should match snapshot when the modal is active color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9)); } +.emotion-13:focus { + box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) var( + --wp-components-color-accent, + var( --wp-admin-theme-color, var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9)) ) + ); + outline: 2px solid transparent; +} + .emotion-15 { display: -webkit-box; display: -webkit-flex; From e59ccad232ccd10304840d288ecb407bb20d8b11 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Tue, 20 Jun 2023 16:26:29 +0300 Subject: [PATCH 129/163] hide the helper navigation block and its wrapping editor (#50662) --- .../navigation-menu-content.js | 5 ++--- .../sidebar-navigation-screen-navigation-menus/style.scss | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js index dd9256b3a5c875..35a06b53786ec7 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js @@ -9,7 +9,6 @@ import { } from '@wordpress/block-editor'; import { useDispatch, useSelect } from '@wordpress/data'; import { createBlock } from '@wordpress/blocks'; -import { VisuallyHidden } from '@wordpress/components'; import { useCallback } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; @@ -106,11 +105,11 @@ export default function NavigationMenuContent( { rootClientId } ) { showAppender={ false } /> ) } -