diff --git a/backport-changelog/6.8/7488.md b/backport-changelog/6.8/7488.md deleted file mode 100644 index a588bef0e01796..00000000000000 --- a/backport-changelog/6.8/7488.md +++ /dev/null @@ -1,3 +0,0 @@ -https://github.com/WordPress/wordpress-develop/pull/7488 - -* https://github.com/WordPress/gutenberg/pull/60622 \ No newline at end of file diff --git a/backport-changelog/6.8/7498.md b/backport-changelog/6.8/7498.md deleted file mode 100644 index 6c903246166b64..00000000000000 --- a/backport-changelog/6.8/7498.md +++ /dev/null @@ -1,3 +0,0 @@ -https://github.com/WordPress/wordpress-develop/pull/7498 - -* https://github.com/WordPress/gutenberg/pull/60622 \ No newline at end of file diff --git a/lib/compat/wordpress-6.8/block-comments.php b/lib/experimental/block-comments.php similarity index 85% rename from lib/compat/wordpress-6.8/block-comments.php rename to lib/experimental/block-comments.php index ff9f03b69aa1c3..3381882589ef6d 100644 --- a/lib/compat/wordpress-6.8/block-comments.php +++ b/lib/experimental/block-comments.php @@ -1,6 +1,6 @@ query_vars['type'] ) && '' === $query->query_vars['type'] ) { diff --git a/lib/compat/wordpress-6.8/class-gutenberg-rest-comment-controller-6-8.php b/lib/experimental/class-gutenberg-rest-comment-controller.php similarity index 87% rename from lib/compat/wordpress-6.8/class-gutenberg-rest-comment-controller-6-8.php rename to lib/experimental/class-gutenberg-rest-comment-controller.php index 60b45b1a63adef..8409c595032961 100644 --- a/lib/compat/wordpress-6.8/class-gutenberg-rest-comment-controller-6-8.php +++ b/lib/experimental/class-gutenberg-rest-comment-controller.php @@ -7,7 +7,7 @@ */ // Create a new class that extends WP_REST_Comments_Controller -class Gutenberg_REST_Comment_Controller_6_8 extends WP_REST_Comments_Controller { +class Gutenberg_REST_Comment_Controller extends WP_REST_Comments_Controller { public function create_item_permissions_check( $request ) { if ( empty( $request['comment_type'] ) || 'comment' === $request['comment_type'] ) { @@ -18,7 +18,7 @@ public function create_item_permissions_check( $request ) { if ( get_option( 'comment_registration' ) ) { return new WP_Error( 'rest_comment_login_required', - __( 'Sorry, you must be logged in to comment.' ), + __( 'Sorry, you must be logged in to comment.', 'gutenberg' ), array( 'status' => 401 ) ); } @@ -40,7 +40,7 @@ public function create_item_permissions_check( $request ) { if ( ! $allow_anonymous ) { return new WP_Error( 'rest_comment_login_required', - __( 'Sorry, you must be logged in to comment.' ), + __( 'Sorry, you must be logged in to comment.', 'gutenberg' ), array( 'status' => 401 ) ); } @@ -51,7 +51,7 @@ public function create_item_permissions_check( $request ) { return new WP_Error( 'rest_comment_invalid_author', /* translators: %s: Request parameter. */ - sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'author' ), + sprintf( __( "Sorry, you are not allowed to edit '%s' for comments.", 'gutenberg' ), 'author' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -61,7 +61,7 @@ public function create_item_permissions_check( $request ) { return new WP_Error( 'rest_comment_invalid_author_ip', /* translators: %s: Request parameter. */ - sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'author_ip' ), + sprintf( __( "Sorry, you are not allowed to edit '%s' for comments.", 'gutenberg' ), 'author_ip' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -71,7 +71,7 @@ public function create_item_permissions_check( $request ) { return new WP_Error( 'rest_comment_invalid_status', /* translators: %s: Request parameter. */ - sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'status' ), + sprintf( __( "Sorry, you are not allowed to edit '%s' for comments.", 'gutenberg' ), 'status' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -79,7 +79,7 @@ public function create_item_permissions_check( $request ) { if ( empty( $request['post'] ) ) { return new WP_Error( 'rest_comment_invalid_post_id', - __( 'Sorry, you are not allowed to create this comment without a post.' ), + __( 'Sorry, you are not allowed to create this comment without a post.', 'gutenberg' ), array( 'status' => 403 ) ); } @@ -89,7 +89,7 @@ public function create_item_permissions_check( $request ) { if ( ! $post ) { return new WP_Error( 'rest_comment_invalid_post_id', - __( 'Sorry, you are not allowed to create this comment without a post.' ), + __( 'Sorry, you are not allowed to create this comment without a post.', 'gutenberg' ), array( 'status' => 403 ) ); } @@ -97,7 +97,7 @@ public function create_item_permissions_check( $request ) { if ( 'trash' === $post->post_status ) { return new WP_Error( 'rest_comment_trash_post', - __( 'Sorry, you are not allowed to create a comment on this post.' ), + __( 'Sorry, you are not allowed to create a comment on this post.', 'gutenberg' ), array( 'status' => 403 ) ); } @@ -105,7 +105,7 @@ public function create_item_permissions_check( $request ) { if ( ! $this->check_read_post_permission( $post, $request ) ) { return new WP_Error( 'rest_cannot_read_post', - __( 'Sorry, you are not allowed to read the post for this comment.' ), + __( 'Sorry, you are not allowed to read the post for this comment.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -117,7 +117,7 @@ public function create_item_permissions_check( $request ) { add_action( 'rest_api_init', function () { - $controller = new Gutenberg_REST_Comment_Controller_6_8(); + $controller = new Gutenberg_REST_Comment_Controller(); $controller->register_routes(); } ); diff --git a/lib/load.php b/lib/load.php index 69ba59e3718842..789c9f9980e9a3 100644 --- a/lib/load.php +++ b/lib/load.php @@ -42,8 +42,6 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.7/rest-api.php'; // WordPress 6.8 compat. - require __DIR__ . '/compat/wordpress-6.8/block-comments.php'; - require __DIR__ . '/compat/wordpress-6.8/class-gutenberg-rest-comment-controller-6-8.php'; require __DIR__ . '/compat/wordpress-6.8/class-gutenberg-hierarchical-sort.php'; require __DIR__ . '/compat/wordpress-6.8/rest-api.php'; @@ -54,6 +52,12 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/experimental/rest-api.php'; require_once __DIR__ . '/experimental/kses-allowed-html.php'; + + // Block Comments. + if ( gutenberg_is_experiment_enabled( 'gutenberg-block-comment' ) ) { + require __DIR__ . '/experimental/block-comments.php'; + require __DIR__ . '/experimental/class-gutenberg-rest-comment-controller.php'; + } } // Experimental signaling server. diff --git a/packages/block-editor/src/hooks/contrast-checker.js b/packages/block-editor/src/hooks/contrast-checker.js index 368e2e75264858..7cb231110401d4 100644 --- a/packages/block-editor/src/hooks/contrast-checker.js +++ b/packages/block-editor/src/hooks/contrast-checker.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useState, useEffect } from '@wordpress/element'; +import { useLayoutEffect, useReducer } from '@wordpress/element'; /** * Internal dependencies @@ -9,52 +9,86 @@ import { useState, useEffect } from '@wordpress/element'; import ContrastChecker from '../components/contrast-checker'; import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs'; -function getComputedStyle( node ) { - return node.ownerDocument.defaultView.getComputedStyle( node ); +function getComputedValue( node, property ) { + return node.ownerDocument.defaultView + .getComputedStyle( node ) + .getPropertyValue( property ); +} + +function getBlockElementColors( blockEl ) { + if ( ! blockEl ) { + return {}; + } + + const firstLinkElement = blockEl.querySelector( 'a' ); + const linkColor = !! firstLinkElement?.innerText + ? getComputedValue( firstLinkElement, 'color' ) + : undefined; + + const textColor = getComputedValue( blockEl, 'color' ); + + let backgroundColorNode = blockEl; + let backgroundColor = getComputedValue( + backgroundColorNode, + 'background-color' + ); + while ( + backgroundColor === 'rgba(0, 0, 0, 0)' && + backgroundColorNode.parentNode && + backgroundColorNode.parentNode.nodeType === + backgroundColorNode.parentNode.ELEMENT_NODE + ) { + backgroundColorNode = backgroundColorNode.parentNode; + backgroundColor = getComputedValue( + backgroundColorNode, + 'background-color' + ); + } + + return { + textColor, + backgroundColor, + linkColor, + }; +} + +function reducer( prevColors, newColors ) { + const hasChanged = Object.keys( newColors ).some( + ( key ) => prevColors[ key ] !== newColors[ key ] + ); + + // Do not re-render if the colors have not changed. + return hasChanged ? newColors : prevColors; } export default function BlockColorContrastChecker( { clientId } ) { - const [ detectedBackgroundColor, setDetectedBackgroundColor ] = useState(); - const [ detectedColor, setDetectedColor ] = useState(); - const [ detectedLinkColor, setDetectedLinkColor ] = useState(); const blockEl = useBlockElement( clientId ); + const [ colors, setColors ] = useReducer( reducer, {} ); // There are so many things that can change the color of a block // So we perform this check on every render. - useEffect( () => { + useLayoutEffect( () => { if ( ! blockEl ) { return; } - setDetectedColor( getComputedStyle( blockEl ).color ); - - const firstLinkElement = blockEl.querySelector( 'a' ); - if ( firstLinkElement && !! firstLinkElement.innerText ) { - setDetectedLinkColor( getComputedStyle( firstLinkElement ).color ); - } - let backgroundColorNode = blockEl; - let backgroundColor = - getComputedStyle( backgroundColorNode ).backgroundColor; - while ( - backgroundColor === 'rgba(0, 0, 0, 0)' && - backgroundColorNode.parentNode && - backgroundColorNode.parentNode.nodeType === - backgroundColorNode.parentNode.ELEMENT_NODE - ) { - backgroundColorNode = backgroundColorNode.parentNode; - backgroundColor = - getComputedStyle( backgroundColorNode ).backgroundColor; + function updateColors() { + setColors( getBlockElementColors( blockEl ) ); } - setDetectedBackgroundColor( backgroundColor ); - }, [ blockEl ] ); + // Combine `useLayoutEffect` and two rAF calls to ensure that values are read + // after the current paint but before the next paint. + window.requestAnimationFrame( () => + window.requestAnimationFrame( updateColors ) + ); + } ); return ( ); } diff --git a/packages/block-library/src/comments-pagination/block.json b/packages/block-library/src/comments-pagination/block.json index 28f6c9fdfdb5d9..b29d95bc4f1c9a 100644 --- a/packages/block-library/src/comments-pagination/block.json +++ b/packages/block-library/src/comments-pagination/block.json @@ -18,6 +18,11 @@ "default": "none" } }, + "example": { + "attributes": { + "paginationArrow": "none" + } + }, "providesContext": { "comments/paginationArrow": "paginationArrow" }, diff --git a/packages/editor/src/components/document-bar/style.scss b/packages/editor/src/components/document-bar/style.scss index 749da6ec8c9837..4b84d8b44d39f3 100644 --- a/packages/editor/src/components/document-bar/style.scss +++ b/packages/editor/src/components/document-bar/style.scss @@ -18,8 +18,10 @@ .components-button { border-radius: $grid-unit-05; - transition: all 0.1s ease-out; - @include reduce-motion("transition"); + + @media not (prefers-reduced-motion) { + transition: all 0.1s ease-out; + } &:hover { background: $gray-200; diff --git a/packages/editor/src/components/document-tools/style.scss b/packages/editor/src/components/document-tools/style.scss index dfafff2126d66d..7f70d02a6eaca0 100644 --- a/packages/editor/src/components/document-tools/style.scss +++ b/packages/editor/src/components/document-tools/style.scss @@ -16,8 +16,9 @@ display: inline-flex; svg { - transition: transform cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s; - @include reduce-motion("transition"); + @media not (prefers-reduced-motion) { + transition: transform cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s; + } } &.is-pressed { diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js index b32fda6d031b14..3a3e231259a57b 100644 --- a/packages/editor/src/components/header/index.js +++ b/packages/editor/src/components/header/index.js @@ -95,11 +95,12 @@ function Header( { [ 'post', 'page', 'wp_template' ].includes( postType ) && hasSectionRootClientId; - const disablePreviewOption = [ - NAVIGATION_POST_TYPE, - TEMPLATE_PART_POST_TYPE, - PATTERN_POST_TYPE, - ].includes( postType ); + const disablePreviewOption = + [ + NAVIGATION_POST_TYPE, + TEMPLATE_PART_POST_TYPE, + PATTERN_POST_TYPE, + ].includes( postType ) || forceDisableBlockTools; const [ isBlockToolsCollapsed, setIsBlockToolsCollapsed ] = useState( true ); diff --git a/packages/editor/src/components/post-featured-image/style.scss b/packages/editor/src/components/post-featured-image/style.scss index bf9433faa662d4..cca45c61adaf8c 100644 --- a/packages/editor/src/components/post-featured-image/style.scss +++ b/packages/editor/src/components/post-featured-image/style.scss @@ -78,12 +78,14 @@ .editor-post-featured-image__actions { &:not(.editor-post-featured-image__actions-missing-image) { - @include reduce-motion("transition"); bottom: 0; opacity: 0; // Use opacity instead of visibility so that the buttons remain in the tab order. padding: $grid-unit-10; position: absolute; - transition: opacity 50ms ease-out; + + @media not (prefers-reduced-motion) { + transition: opacity 50ms ease-out; + } .editor-post-featured-image__action { backdrop-filter: blur(16px) saturate(180%); diff --git a/packages/editor/src/components/post-publish-panel/style.scss b/packages/editor/src/components/post-publish-panel/style.scss index 7b075717651781..6221eaef52767b 100644 --- a/packages/editor/src/components/post-publish-panel/style.scss +++ b/packages/editor/src/components/post-publish-panel/style.scss @@ -210,9 +210,11 @@ left: auto; width: $sidebar-width + $border-width; border-left: $border-width solid $gray-300; - transform: translateX(+100%); - animation: editor-post-publish-panel__slide-in-animation 0.1s forwards; - @include reduce-motion("animation"); + + @media not (prefers-reduced-motion) { + transform: translateX(+100%); + animation: editor-post-publish-panel__slide-in-animation 0.1s forwards; + } body.is-fullscreen-mode & { top: 0; diff --git a/packages/editor/src/components/post-text-editor/style.scss b/packages/editor/src/components/post-text-editor/style.scss index f3b9b65007df75..5391588d906d8d 100644 --- a/packages/editor/src/components/post-text-editor/style.scss +++ b/packages/editor/src/components/post-text-editor/style.scss @@ -10,8 +10,10 @@ textarea.editor-post-text-editor { font-family: $editor-html-font; line-height: 2.4; min-height: 200px; - transition: border 0.1s ease-out, box-shadow 0.1s linear; - @include reduce-motion("transition"); + + @media not (prefers-reduced-motion) { + transition: border 0.1s ease-out, box-shadow 0.1s linear; + } // Same padding as title. padding: $grid-unit-20; diff --git a/packages/editor/src/components/preferences-modal/index.js b/packages/editor/src/components/preferences-modal/index.js index fba60405e7e4b5..fcca1b00e9bb2d 100644 --- a/packages/editor/src/components/preferences-modal/index.js +++ b/packages/editor/src/components/preferences-modal/index.js @@ -250,7 +250,7 @@ function PreferencesModalContents( { extraSections = {} } ) { scope="core" featureName="keepCaretInsideBlock" help={ __( - 'Keeps the text cursor within the block boundaries, aiding users with screen readers by preventing unintentional cursor movement outside the block.' + 'Keeps the text cursor within blocks while navigating with arrow keys, preventing it from moving to other blocks and enhancing accessibility for keyboard users.' ) } label={ __( 'Contain text cursor inside block' diff --git a/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js b/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js index c5eafdafe918db..86c08fe1cd43e3 100644 --- a/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js +++ b/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js @@ -27,7 +27,7 @@ test.describe( 'WP Editor Meta Boxes', () => { // Switch tinymce to Text mode, first waiting for it to initialize // because otherwise it will flip back to Visual mode once initialized. await page.locator( '#test_tinymce_id_ifr' ).waitFor(); - await page.locator( 'role=button[name="Text"i]' ).click(); + await page.locator( 'role=button[name="Code"i]' ).click(); // Type something in the tinymce Text mode textarea. const metaBoxField = page.locator( '#test_tinymce_id' );