diff --git a/.eslintrc.js b/.eslintrc.js index b5349d2de0a1ff..2e92b495691873 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -321,6 +321,7 @@ module.exports = { ...[ 'BorderBoxControl', 'BorderControl', + 'BoxControl', 'ComboboxControl', 'CustomSelectControl', 'DimensionControl', diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index 3efd7d79ee2276..2c3998c2952808 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -28,7 +28,7 @@ jobs: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Use desired version of Java - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0 with: distribution: 'corretto' java-version: '17' @@ -47,7 +47,7 @@ jobs: run: npm run native test:e2e:setup - name: Gradle cache - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - name: AVD cache uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 diff --git a/docs/reference-guides/block-api/block-api-versions.md b/docs/reference-guides/block-api/block-api-versions.md index b4b11e7c23c7cc..d89362777ca58c 100644 --- a/docs/reference-guides/block-api/block-api-versions.md +++ b/docs/reference-guides/block-api/block-api-versions.md @@ -3,7 +3,7 @@ This document lists the changes made between the different API versions. ## Version 3 (>= WordPress 6.3) -- The post editor will be iframed if all registered blocks have a Block API version 3 or higher and the editor has no classic meta boxes below the blocks. Adding version 3 support means that the block should work inside an iframe, though the block may still be rendered outside the iframe if not all blocks support version 3. +- The post editor will be iframed if all registered blocks have a Block API version 3 or higher. Adding version 3 support means that the block should work inside an iframe, though the block may still be rendered outside the iframe if not all blocks support version 3. ## Version 2 (>= WordPress 5.6) diff --git a/lib/class-wp-rest-global-styles-controller-gutenberg.php b/lib/class-wp-rest-global-styles-controller-gutenberg.php index 421408e6f20b4a..e33304e596e129 100644 --- a/lib/class-wp-rest-global-styles-controller-gutenberg.php +++ b/lib/class-wp-rest-global-styles-controller-gutenberg.php @@ -13,25 +13,30 @@ /** * Base Global Styles REST API Controller. */ -class WP_REST_Global_Styles_Controller_Gutenberg extends WP_REST_Controller { +class WP_REST_Global_Styles_Controller_Gutenberg extends WP_REST_Posts_Controller { /** - * Post type. + * Whether the controller supports batching. * - * @since 5.9.0 - * @var string + * @since 6.6.0 + * @var array */ - protected $post_type; + protected $allow_batch = array( 'v1' => false ); /** * Constructor. * * @since 5.9.0 */ - public function __construct() { - $this->namespace = 'wp/v2'; - $this->rest_base = 'global-styles'; - $this->post_type = 'wp_global_styles'; + /** + * Constructor. + * + * @since 6.6.0 + * + * @param string $post_type Post type. + */ + public function __construct( $post_type = 'wp_global_styles' ) { + parent::__construct( $post_type ); } /** @@ -54,8 +59,14 @@ public function register_routes() { 'type' => 'string', ), ), + 'allow_batch' => $this->allow_batch, ), - ) + ), + /* + * $override is set to true to avoid conflicts with the core endpoint. + * Do not sync to WordPress core. + */ + true ); // List themes global styles. @@ -65,8 +76,10 @@ public function register_routes() { sprintf( '/%s/themes/(?P%s)', $this->rest_base, - // Matches theme's directory: `/themes///` or `/themes//`. - // Excludes invalid directory name characters: `/:<>*?"|`. + /* + * Matches theme's directory: `/themes///` or `/themes//`. + * Excludes invalid directory name characters: `/:<>*?"|`. + */ '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?' ), array( @@ -81,8 +94,14 @@ public function register_routes() { 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ), ), ), + 'allow_batch' => $this->allow_batch, ), - ) + ), + /* + * $override is set to true to avoid conflicts with the core endpoint. + * Do not sync to WordPress core. + */ + true ); // Lists/updates a single global style variation based on the given id. @@ -108,8 +127,14 @@ public function register_routes() { 'permission_callback' => array( $this, 'update_item_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) + 'schema' => array( $this, 'get_public_item_schema' ), + 'allow_batch' => $this->allow_batch, + ), + /* + * $override is set to true to avoid conflicts with the core endpoint. + * Do not sync to WordPress core. + */ + true ); } @@ -196,28 +221,10 @@ public function get_item_permissions_check( $request ) { * @param WP_Post $post Post object. * @return bool Whether the post can be read. */ - protected function check_read_permission( $post ) { + public function check_read_permission( $post ) { return current_user_can( 'read_post', $post->ID ); } - /** - * Returns the given global styles config. - * - * @since 5.9.0 - * - * @param WP_REST_Request $request The request instance. - * - * @return WP_REST_Response|WP_Error - */ - public function get_item( $request ) { - $post = $this->get_post( $request['id'] ); - if ( is_wp_error( $post ) ) { - return $post; - } - - return $this->prepare_item_for_response( $post, $request ); - } - /** * Checks if a given request has access to write a single global styles config. * @@ -243,61 +250,12 @@ public function update_item_permissions_check( $request ) { return true; } - /** - * Checks if a global style can be edited. - * - * @since 5.9.0 - * - * @param WP_Post $post Post object. - * @return bool Whether the post can be edited. - */ - protected function check_update_permission( $post ) { - return current_user_can( 'edit_post', $post->ID ); - } - - /** - * Updates a single global style config. - * - * @since 5.9.0 - * @since 6.2.0 Added validation of styles.css property. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function update_item( $request ) { - $post_before = $this->get_post( $request['id'] ); - if ( is_wp_error( $post_before ) ) { - return $post_before; - } - - $changes = $this->prepare_item_for_database( $request ); - if ( is_wp_error( $changes ) ) { - return $changes; - } - - $result = wp_update_post( wp_slash( (array) $changes ), true, false ); - if ( is_wp_error( $result ) ) { - return $result; - } - - $post = get_post( $request['id'] ); - $fields_update = $this->update_additional_fields_for_object( $post, $request ); - if ( is_wp_error( $fields_update ) ) { - return $fields_update; - } - - wp_after_insert_post( $post, true, $post_before ); - - $response = $this->prepare_item_for_response( $post, $request ); - - return rest_ensure_response( $response ); - } - /** * Prepares a single global styles config for update. * * @since 5.9.0 * @since 6.2.0 Added validation of styles.css property. + * @since 6.6.0 Added registration of block style variations from theme.json sources (theme.json, user theme.json, partials). * * @param WP_REST_Request $request Request object. * @return stdClass|WP_Error Prepared item on success. WP_Error on when the custom CSS is not valid. @@ -394,10 +352,12 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V } if ( rest_is_field_included( 'title.rendered', $fields ) ) { add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + add_filter( 'private_title_format', array( $this, 'protected_title_format' ) ); $data['title']['rendered'] = get_the_title( $post->ID ); remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + remove_filter( 'private_title_format', array( $this, 'protected_title_format' ) ); } if ( rest_is_field_included( 'settings', $fields ) ) { @@ -426,7 +386,7 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V } $response->add_links( $links ); if ( ! empty( $links['self']['href'] ) ) { - $actions = $this->get_available_actions(); + $actions = $this->get_available_actions( $post, $request ); $self = $links['self']['href']; foreach ( $actions as $rel ) { $response->add_link( $rel, $self ); @@ -450,9 +410,12 @@ protected function prepare_links( $id ) { $base = sprintf( '%s/%s', $this->namespace, $this->rest_base ); $links = array( - 'self' => array( + 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $id ), ), + 'about' => array( + 'href' => rest_url( 'wp/v2/types/' . $this->post_type ), + ), ); if ( post_type_supports( $this->post_type, 'revisions' ) ) { @@ -473,13 +436,16 @@ protected function prepare_links( $id ) { * * @since 5.9.0 * @since 6.2.0 Added 'edit-css' action. + * @since 6.6.0 Added $post and $request parameters. * + * @param WP_Post $post Post object. + * @param WP_REST_Request $request Request object. * @return array List of link relations. */ - protected function get_available_actions() { + protected function get_available_actions( $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $rels = array(); - $post_type = get_post_type_object( $this->post_type ); + $post_type = get_post_type_object( $post->post_type ); if ( current_user_can( $post_type->cap->publish_posts ) ) { $rels[] = 'https://api.w.org/action-publish'; } @@ -491,21 +457,6 @@ protected function get_available_actions() { return $rels; } - /** - * Overwrites the default protected title format. - * - * By default, WordPress will show password protected posts with a title of - * "Protected: %s", as the REST API communicates the protected status of a post - * in a machine readable format, we remove the "Protected: " prefix. - * - * @since 5.9.0 - * - * @return string Protected title format. - */ - public function protected_title_format() { - return '%s'; - } - /** * Retrieves the query params for the global styles collection. * @@ -589,7 +540,7 @@ public function get_theme_item_permissions_check( $request ) { // phpcs:ignore V /* * Verify if the current user has edit_theme_options capability. - * This capability is required to edit/view/delete templates. + * This capability is required to edit/view/delete global styles. */ if ( ! current_user_can( 'edit_theme_options' ) ) { return new WP_Error( @@ -623,8 +574,8 @@ public function get_theme_item( $request ) { } $theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' ); - $data = array(); $fields = $this->get_fields_for_response( $request ); + $data = array(); if ( rest_is_field_included( 'settings', $fields ) ) { $data['settings'] = $theme->get_settings(); @@ -669,7 +620,7 @@ public function get_theme_items_permissions_check( $request ) { // phpcs:ignore /* * Verify if the current user has edit_theme_options capability. - * This capability is required to edit/view/delete templates. + * This capability is required to edit/view/delete global styles. */ if ( ! current_user_can( 'edit_theme_options' ) ) { return new WP_Error( diff --git a/lib/rest-api.php b/lib/rest-api.php index 0becbc4a72d356..ac020e243ec056 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -11,14 +11,24 @@ } /** - * Registers the Global Styles REST API routes. + * Overrides the REST controller for the `wp_global_styles` post type. + * + * @param array $args Array of arguments for registering a post type. + * See the register_post_type() function for accepted arguments. + * @param string $post_type Post type key. + * + * @return array Array of arguments for registering a post type. */ -function gutenberg_register_global_styles_endpoints() { - $global_styles_controller = new WP_REST_Global_Styles_Controller_Gutenberg(); - $global_styles_controller->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); +function gutenberg_override_global_styles_endpoint( array $args ): array { + $args['rest_controller_class'] = 'WP_REST_Global_Styles_Controller_Gutenberg'; + $args['revisions_rest_controller_class'] = 'Gutenberg_REST_Global_Styles_Revisions_Controller_6_6'; + $args['late_route_registration'] = true; + $args['show_in_rest'] = true; + $args['rest_base'] = 'global-styles'; + return $args; +} +add_filter( 'register_wp_global_styles_post_type_args', 'gutenberg_override_global_styles_endpoint', 10, 2 ); /** * Registers the Edit Site Export REST API routes. diff --git a/packages/block-editor/src/components/image-editor/use-save-image.js b/packages/block-editor/src/components/image-editor/use-save-image.js index ff91a9794ca158..094ce1600545b5 100644 --- a/packages/block-editor/src/components/image-editor/use-save-image.js +++ b/packages/block-editor/src/components/image-editor/use-save-image.js @@ -54,6 +54,13 @@ export default function useSaveImage( { } ); } + if ( modifiers.length === 0 ) { + // No changes to apply. + setIsInProgress( false ); + onFinishEditing(); + return; + } + apiFetch( { path: `/wp/v2/media/${ id }/edit`, method: 'POST', diff --git a/packages/block-library/src/embed/embed-preview.js b/packages/block-library/src/embed/embed-preview.js index 6ef10df08a8c24..1a230faf0c3d57 100644 --- a/packages/block-library/src/embed/embed-preview.js +++ b/packages/block-library/src/embed/embed-preview.js @@ -14,7 +14,7 @@ import clsx from 'clsx'; import { __, sprintf } from '@wordpress/i18n'; import { Placeholder, SandBox } from '@wordpress/components'; import { BlockIcon } from '@wordpress/block-editor'; -import { Component } from '@wordpress/element'; +import { useState } from '@wordpress/element'; import { getAuthority } from '@wordpress/url'; /** @@ -23,129 +23,113 @@ import { getAuthority } from '@wordpress/url'; import WpEmbedPreview from './wp-embed-preview'; import { Caption } from '../utils/caption'; -class EmbedPreview extends Component { - constructor() { - super( ...arguments ); - this.hideOverlay = this.hideOverlay.bind( this ); - this.state = { - interactive: false, - }; - } - - static getDerivedStateFromProps( nextProps, state ) { - if ( ! nextProps.isSelected && state.interactive ) { - // We only want to change this when the block is not selected, because changing it when - // the block becomes selected makes the overlap disappear too early. Hiding the overlay - // happens on mouseup when the overlay is clicked. - return { interactive: false }; - } +export default function EmbedPreview( { + preview, + previewable, + url, + type, + isSelected, + className, + icon, + label, + insertBlocksAfter, + attributes, + setAttributes, +} ) { + const [ interactive, setInteractive ] = useState( false ); - return null; + if ( ! isSelected && interactive ) { + // We only want to change this when the block is not selected, because changing it when + // the block becomes selected makes the overlap disappear too early. Hiding the overlay + // happens on mouseup when the overlay is clicked. + setInteractive( false ); } - hideOverlay() { + const hideOverlay = () => { // This is called onMouseUp on the overlay. We can't respond to the `isSelected` prop // changing, because that happens on mouse down, and the overlay immediately disappears, // and the mouse event can end up in the preview content. We can't use onClick on // the overlay to hide it either, because then the editor misses the mouseup event, and // thinks we're multi-selecting blocks. - this.setState( { interactive: true } ); - } + setInteractive( true ); + }; - render() { - const { - preview, - previewable, - url, - type, - className, - icon, - label, - insertBlocksAfter, - attributes, - setAttributes, - isSelected, - } = this.props; - const { scripts } = preview; - const { interactive } = this.state; + const { scripts } = preview; - const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html; - const embedSourceUrl = getAuthority( url ); - const iframeTitle = sprintf( - // translators: %s: host providing embed content e.g: www.youtube.com - __( 'Embedded content from %s' ), - embedSourceUrl - ); - const sandboxClassnames = clsx( - type, - className, - 'wp-block-embed__wrapper' - ); + const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html; + const embedSourceUrl = getAuthority( url ); + const iframeTitle = sprintf( + // translators: %s: host providing embed content e.g: www.youtube.com + __( 'Embedded content from %s' ), + embedSourceUrl + ); + const sandboxClassnames = clsx( + type, + className, + 'wp-block-embed__wrapper' + ); - // Disabled because the overlay div doesn't actually have a role or functionality - // as far as the user is concerned. We're just catching the first click so that - // the block can be selected without interacting with the embed preview that the overlay covers. - /* eslint-disable jsx-a11y/no-static-element-interactions */ - const embedWrapper = - 'wp-embed' === type ? ( - - ) : ( -
- + ) : ( +
+ + { ! interactive && ( +
- { ! interactive && ( -
- ) } -
- ); - /* eslint-enable jsx-a11y/no-static-element-interactions */ - - return ( -
- { previewable ? ( - embedWrapper - ) : ( - } - label={ label } - > -

- { url } -

-

- { sprintf( - /* translators: %s: host providing embed content e.g: www.youtube.com */ - __( - "Embedded content from %s can't be previewed in the editor." - ), - embedSourceUrl - ) } -

-
) } - -
+
); - } -} + /* eslint-enable jsx-a11y/no-static-element-interactions */ -export default EmbedPreview; + return ( +
+ { previewable ? ( + embedWrapper + ) : ( + } + label={ label } + > +

+ { url } +

+

+ { sprintf( + /* translators: %s: host providing embed content e.g: www.youtube.com */ + __( + "Embedded content from %s can't be previewed in the editor." + ), + embedSourceUrl + ) } +

+
+ ) } + +
+ ); +} diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 04858e448da7f0..1473cf5f5df40e 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -19,6 +19,7 @@ - `Navigator`: warn if a screen's `path` doesn't follow a URL-like scheme ([#65231](https://github.com/WordPress/gutenberg/pull/65231)). - `Modal`: Decrease close button size and remove horizontal offset ([#65131](https://github.com/WordPress/gutenberg/pull/65131)). +- `Card`: Adopt radius scale ([#65053](https://github.com/WordPress/gutenberg/pull/65053)). ### Bug Fixes diff --git a/packages/components/src/box-control/README.md b/packages/components/src/box-control/README.md index 2fd214b79157f7..b03b03a85466ae 100644 --- a/packages/components/src/box-control/README.md +++ b/packages/components/src/box-control/README.md @@ -100,3 +100,10 @@ A handler for onMouseOver events. A handler for onMouseOut events. - Required: No + +### `__next40pxDefaultSize`: `boolean` + +Start opting into the larger default size that will become the default size in a future version. + +- Required: No +- Default: `false` diff --git a/packages/components/src/button-group/stories/index.story.tsx b/packages/components/src/button-group/stories/index.story.tsx index 958a0d137763e9..f6af2416977f40 100644 --- a/packages/components/src/button-group/stories/index.story.tsx +++ b/packages/components/src/button-group/stories/index.story.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import type { Meta, StoryFn } from '@storybook/react'; +import type { Meta, StoryObj } from '@storybook/react'; /** * Internal dependencies @@ -22,18 +22,13 @@ const meta: Meta< typeof ButtonGroup > = { }; export default meta; -const Template: StoryFn< typeof ButtonGroup > = ( args ) => { - const style = { margin: '0 4px' }; - return ( - - - - - ); +export const Default: StoryObj< typeof ButtonGroup > = { + args: { + children: ( + <> + + + + ), + }, }; - -export const Default: StoryFn< typeof ButtonGroup > = Template.bind( {} ); diff --git a/packages/components/src/card/card/component.tsx b/packages/components/src/card/card/component.tsx index 8fefc33bd48027..e459f9abb9275b 100644 --- a/packages/components/src/card/card/component.tsx +++ b/packages/components/src/card/card/component.tsx @@ -34,7 +34,7 @@ function UnconnectedCard( size, ...otherProps } = useCard( props ); - const elevationBorderRadius = isRounded ? CONFIG.cardBorderRadius : 0; + const elevationBorderRadius = isRounded ? CONFIG.radiusLarge : 0; const cx = useCx(); diff --git a/packages/components/src/card/styles.ts b/packages/components/src/card/styles.ts index fa70b19054e2fc..df0fe4eb7ed720 100644 --- a/packages/components/src/card/styles.ts +++ b/packages/components/src/card/styles.ts @@ -12,7 +12,7 @@ import { COLORS, CONFIG } from '../utils'; // (as opposed to the `border` property), the value of the border radius needs // to be adjusted by removing 1px (this is because the `box-shadow` renders // as an "outer radius"). -const adjustedBorderRadius = `calc(${ CONFIG.cardBorderRadius } - 1px)`; +const adjustedBorderRadius = `calc(${ CONFIG.radiusLarge } - 1px)`; export const Card = css` box-shadow: 0 0 0 1px ${ CONFIG.surfaceBorderColor }; diff --git a/packages/components/src/card/test/__snapshots__/index.tsx.snap b/packages/components/src/card/test/__snapshots__/index.tsx.snap index cc1a754e1d37c9..e9073edba3e3e4 100644 --- a/packages/components/src/card/test/__snapshots__/index.tsx.snap +++ b/packages/components/src/card/test/__snapshots__/index.tsx.snap @@ -8,8 +8,8 @@ Snapshot Diff: @@ -1,8 +1,8 @@
@@ -25,8 +25,8 @@ Snapshot Diff: @@ -1,8 +1,8 @@
@@ -42,8 +42,8 @@ Snapshot Diff: @@ -1,8 +1,8 @@