diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 8f823a543b826e..7d2c780599cddf 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -169,7 +169,7 @@ jobs: steps: - name: Install Subversion run: | - apt-get update -y && apt-get install -y subversion + sudo apt-get update -y && sudo apt-get install -y subversion - name: Check out Gutenberg trunk from WP.org plugin repo run: | @@ -228,7 +228,7 @@ jobs: steps: - name: Install Subversion run: | - apt-get update -y && apt-get install -y subversion + sudo apt-get update -y && sudo apt-get install -y subversion - name: Download and unzip Gutenberg plugin asset into tags folder env: diff --git a/backport-changelog/6.8/6910.md b/backport-changelog/6.9/6910.md similarity index 100% rename from backport-changelog/6.8/6910.md rename to backport-changelog/6.9/6910.md diff --git a/changelog.txt b/changelog.txt index e0ff271a5b28d8..e791d102a6b773 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,15 @@ == Changelog == -= 20.2.0-rc.1 = += 20.0.1 = +## Changelog + +### Bug Fixes + +- iAPI Router: add missing changelog entry for [#68923](https://github.com/WordPress/gutenberg/pull/68945) + + += 20.2.0 = ## Changelog @@ -143,6 +151,8 @@ The following contributors merged PRs in this release: @afercia @benazeer-ben @fabiankaegy @himanshupathak95 @im3dabasia @Infinite-Null @justlevine @karthick-murugan @Mamaduka @Mayank-Tripathi32 @SainathPoojary @shail-mehta @shimotmk @Soean @spacedmonkey @stokesman @Sukhendu2002 @t-hamano @yogeshbhutkar + + = 20.1.0 = ## Changelog diff --git a/gutenberg.php b/gutenberg.php index 06152fa72876e1..de187b8d13af90 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.6 * Requires PHP: 7.2 - * Version: 20.2.0-rc.1 + * Version: 20.2.0 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 8c8ec35e2c3a99..38b92c08b9a792 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gutenberg", - "version": "20.2.0-rc.1", + "version": "20.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gutenberg", - "version": "20.2.0-rc.1", + "version": "20.2.0", "hasInstallScript": true, "license": "GPL-2.0-or-later", "workspaces": [ @@ -45328,9 +45328,9 @@ } }, "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz", + "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==", "license": "MIT", "dependencies": { "@fastify/busboy": "^2.0.0" @@ -49960,7 +49960,7 @@ }, "packages/block-directory": { "name": "@wordpress/block-directory", - "version": "5.17.0", + "version": "5.17.1", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -51024,7 +51024,7 @@ }, "packages/edit-post": { "name": "@wordpress/edit-post", - "version": "8.17.0", + "version": "8.17.1", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -51071,7 +51071,7 @@ }, "packages/edit-site": { "name": "@wordpress/edit-site", - "version": "6.17.0", + "version": "6.17.1", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", @@ -51177,7 +51177,7 @@ }, "packages/editor": { "name": "@wordpress/editor", - "version": "14.17.0", + "version": "14.17.1", "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "7.25.7", diff --git a/package.json b/package.json index b5fd51d6edd49b..3d0194fb941bb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "20.2.0-rc.1", + "version": "20.2.0", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", diff --git a/packages/api-fetch/src/middlewares/preloading.js b/packages/api-fetch/src/middlewares/preloading.js index ac293738513bd3..3eaab48dc8392c 100644 --- a/packages/api-fetch/src/middlewares/preloading.js +++ b/packages/api-fetch/src/middlewares/preloading.js @@ -68,15 +68,40 @@ function createPreloadingMiddleware( preloadedData ) { * @return {Promise} Promise with the response. */ function prepareResponse( responseData, parse ) { - return Promise.resolve( - parse - ? responseData.body - : new window.Response( JSON.stringify( responseData.body ), { - status: 200, - statusText: 'OK', - headers: responseData.headers, - } ) - ); + if ( parse ) { + return Promise.resolve( responseData.body ); + } + + try { + return Promise.resolve( + new window.Response( JSON.stringify( responseData.body ), { + status: 200, + statusText: 'OK', + headers: responseData.headers, + } ) + ); + } catch { + // See: https://github.com/WordPress/gutenberg/issues/67358#issuecomment-2621163926. + Object.entries( responseData.headers ).forEach( ( [ key, value ] ) => { + if ( key.toLowerCase() === 'link' ) { + responseData.headers[ key ] = value.replace( + /<([^>]+)>/, + ( /** @type {any} */ _, /** @type {string} */ url ) => + `<${ encodeURI( url ) }>` + ); + } + } ); + + return Promise.resolve( + parse + ? responseData.body + : new window.Response( JSON.stringify( responseData.body ), { + status: 200, + statusText: 'OK', + headers: responseData.headers, + } ) + ); + } } export default createPreloadingMiddleware; diff --git a/packages/api-fetch/src/middlewares/test/preloading.js b/packages/api-fetch/src/middlewares/test/preloading.js index 696d8a37648651..a3ffedbdef46d5 100644 --- a/packages/api-fetch/src/middlewares/test/preloading.js +++ b/packages/api-fetch/src/middlewares/test/preloading.js @@ -265,6 +265,57 @@ describe( 'Preloading Middleware', () => { expect( secondMiddleware ).toHaveBeenCalledTimes( 1 ); } ); + it( 'should not throw an error when non-ASCII headers are present', async () => { + const noResponseMock = 'undefined' === typeof window.Response; + if ( noResponseMock ) { + window.Response = class { + constructor( body, options ) { + this.body = JSON.parse( body ); + this.headers = options.headers; + + // Check for non-ASCII characters in headers + for ( const [ key, value ] of Object.entries( + this.headers || {} + ) ) { + if ( /[^\x00-\x7F]/.test( value ) ) { + throw new Error( + `Invalid non-ASCII character found in header: ${ key }` + ); + } + } + } + }; + } + + const data = { + body: 'Hello', + headers: { + Link: '; rel="alternate"; type=text/html', + }, + }; + + const preloadedData = { + 'wp/v2/example': data, + }; + + const preloadingMiddleware = + createPreloadingMiddleware( preloadedData ); + + const requestOptions = { + method: 'GET', + path: 'wp/v2/example', + parse: false, + }; + + await expect( + preloadingMiddleware( requestOptions, () => {} ) + ).resolves.not.toThrow(); + + if ( noResponseMock ) { + delete window.Response; + } + } ); + describe.each( [ [ 'GET' ], [ 'OPTIONS' ] ] )( '%s', ( method ) => { describe.each( [ [ 'all empty', {} ], diff --git a/packages/babel-preset-default/polyfill-exclusions.js b/packages/babel-preset-default/polyfill-exclusions.js index ca8c045d124146..9d0d18737540cb 100644 --- a/packages/babel-preset-default/polyfill-exclusions.js +++ b/packages/babel-preset-default/polyfill-exclusions.js @@ -28,4 +28,9 @@ module.exports = [ // // @see https://github.com/WordPress/gutenberg/pull/67230 /^es(next)?\.set\./, + // Remove Iterator feature polyfills. + // For the same reasoning as for Set exlusion above, we're excluding all iterator helper polyfills. + // + // @see https://github.com/WordPress/wordpress-develop/pull/8224#issuecomment-2636390007. + /^es(next)?\.iterator\./, ]; diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index b3af341dba0f37..f7750c2e49f170 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "5.17.0", + "version": "5.17.1", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-directory/src/plugins/index.js b/packages/block-directory/src/plugins/index.js index 283ff0ea4d2715..50418a9944d311 100644 --- a/packages/block-directory/src/plugins/index.js +++ b/packages/block-directory/src/plugins/index.js @@ -13,6 +13,9 @@ import InstalledBlocksPrePublishPanel from './installed-blocks-pre-publish-panel import getInstallMissing from './get-install-missing'; registerPlugin( 'block-directory', { + // The icon is explicitly set to undefined to prevent PluginPrePublishPanel + // from rendering the fallback icon pluginIcon. + icon: undefined, render() { return ( <> diff --git a/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js b/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js index 17630c9904e8bd..233f3261c79fc5 100644 --- a/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js +++ b/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js @@ -3,7 +3,6 @@ */ import { _n, sprintf } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; -import { blockDefault } from '@wordpress/icons'; import { PluginPrePublishPanel } from '@wordpress/editor'; /** @@ -24,7 +23,6 @@ export default function InstalledBlocksPrePublishPanel() { return ( { diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss index fe0893b0619a72..20aa1c288103d4 100644 --- a/packages/block-editor/src/components/inserter/style.scss +++ b/packages/block-editor/src/components/inserter/style.scss @@ -168,10 +168,6 @@ $block-inserter-tabs-height: 44px; text-align: center; } -.block-editor-inserter__no-results-icon { - fill: $gray-600; -} - .block-editor-inserter__child-blocks { padding: 0 $grid-unit-20; } diff --git a/packages/block-library/src/post-featured-image/dimension-controls.js b/packages/block-library/src/post-featured-image/dimension-controls.js index 9a71a96b2db846..4bc343635f2f93 100644 --- a/packages/block-library/src/post-featured-image/dimension-controls.js +++ b/packages/block-library/src/post-featured-image/dimension-controls.js @@ -10,19 +10,7 @@ import { __experimentalUseCustomUnits as useCustomUnits, __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; -import { - useSettings, - privateApis as blockEditorPrivateApis, - store as blockEditorStore, -} from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { unlock } from '../lock-unlock'; - -const { ResolutionTool } = unlock( blockEditorPrivateApis ); +import { useSettings } from '@wordpress/block-editor'; const SCALE_OPTIONS = ( <> @@ -45,7 +33,6 @@ const SCALE_OPTIONS = ( ); const DEFAULT_SCALE = 'cover'; -const DEFAULT_SIZE = 'full'; const scaleHelp = { cover: __( @@ -61,9 +48,8 @@ const scaleHelp = { const DimensionControls = ( { clientId, - attributes: { aspectRatio, width, height, scale, sizeSlug }, + attributes: { aspectRatio, width, height, scale }, setAttributes, - media, } ) => { const [ availableUnits, defaultRatios, themeRatios, showDefaultRatios ] = useSettings( @@ -75,18 +61,6 @@ const DimensionControls = ( { const units = useCustomUnits( { availableUnits: availableUnits || [ 'px', '%', 'vw', 'em', 'rem' ], } ); - const imageSizes = useSelect( - ( select ) => select( blockEditorStore ).getSettings().imageSizes, - [] - ); - const imageSizeOptions = imageSizes - .filter( ( { slug } ) => { - return media?.media_details?.sizes?.[ slug ]?.source_url; - } ) - .map( ( { name, slug } ) => ( { - value: slug, - label: name, - } ) ); const onDimensionChange = ( dimension, nextValue ) => { const parsedValue = parseFloat( nextValue ); @@ -230,21 +204,6 @@ const DimensionControls = ( { ) } - { !! imageSizeOptions.length && ( - - setAttributes( { sizeSlug: nextSizeSlug } ) - } - isShownByDefault={ false } - resetAllFilter={ () => ( { - sizeSlug: DEFAULT_SIZE, - } ) } - /> - ) } ); }; diff --git a/packages/block-library/src/post-featured-image/edit.js b/packages/block-library/src/post-featured-image/edit.js index 6afe2c29e25045..9fd64b29908e42 100644 --- a/packages/block-library/src/post-featured-image/edit.js +++ b/packages/block-library/src/post-featured-image/edit.js @@ -27,6 +27,8 @@ import { __experimentalUseBorderProps as useBorderProps, __experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles, useBlockEditingMode, + privateApis as blockEditorPrivateApis, + store as blockEditorStore, } from '@wordpress/block-editor'; import { useMemo, useEffect, useState } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; @@ -40,12 +42,37 @@ import DimensionControls from './dimension-controls'; import OverlayControls from './overlay-controls'; import Overlay from './overlay'; import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; +import { unlock } from '../lock-unlock'; const ALLOWED_MEDIA_TYPES = [ 'image' ]; +const { ResolutionTool } = unlock( blockEditorPrivateApis ); +const DEFAULT_MEDIA_SIZE_SLUG = 'full'; + +function FeaturedImageResolutionTool( { image, value, onChange } ) { + const { imageSizes } = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return { + imageSizes: getSettings().imageSizes, + }; + }, [] ); + + if ( ! imageSizes?.length ) { + return null; + } + + const imageSizeOptions = imageSizes + .filter( + ( { slug } ) => image?.media_details?.sizes?.[ slug ]?.source_url + ) + .map( ( { name, slug } ) => ( { value: slug, label: name } ) ); -function getMediaSourceUrlBySizeSlug( media, slug ) { return ( - media?.media_details?.sizes?.[ slug ]?.source_url || media?.source_url + ); } @@ -125,7 +152,9 @@ export default function PostFeaturedImageEdit( { [ featuredImage, postTypeSlug, postId ] ); - const mediaUrl = getMediaSourceUrlBySizeSlug( media, sizeSlug ); + const mediaUrl = + media?.media_details?.sizes?.[ sizeSlug ]?.source_url || + media?.source_url; const blockProps = useBlockProps( { style: { width, height, aspectRatio }, @@ -291,6 +320,13 @@ export default function PostFeaturedImageEdit( { /> ) } + + setAttributes( { sizeSlug: nextSizeSlug } ) + } + /> diff --git a/packages/block-library/src/social-links/editor.scss b/packages/block-library/src/social-links/editor.scss index 54a4154659eb2d..6b1286c94757d8 100644 --- a/packages/block-library/src/social-links/editor.scss +++ b/packages/block-library/src/social-links/editor.scss @@ -100,6 +100,10 @@ .wp-block-social-links .block-list-appender { position: static; // display inline. + .block-editor-inserter { + font-size: inherit; + } + .block-editor-button-block-appender { height: 1.5em; width: 1.5em; diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 142272bd3ad000..7e9c24c637abcb 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -4,9 +4,9 @@ ### Enhancements +- `ComboboxControl`: Add an `isLoading` prop to show a loading spinner ([#68990](https://github.com/WordPress/gutenberg/pull/68990)) - `BorderControlDropdown`, `BorderControl`: Reset button is always visible. ([#69066](https://github.com/WordPress/gutenberg/pull/69066)). - ## 29.3.0 (2025-01-29) ### Enhancements diff --git a/packages/components/src/combobox-control/README.md b/packages/components/src/combobox-control/README.md index 4089cf9c56e9b5..7a11c8b95fe2e9 100644 --- a/packages/components/src/combobox-control/README.md +++ b/packages/components/src/combobox-control/README.md @@ -39,6 +39,7 @@ function MyComboboxControl() { label="Font Size" value={ fontSize } onChange={ setFontSize } + isLoading={ isLoading } options={ filteredOptions } onFilterValueChange={ ( inputValue ) => setFilteredOptions( @@ -112,13 +113,20 @@ If the control is clicked, the dropdown will expand regardless of this prop. - Required: No - Default: `true` -### placeholder +#### placeholder If passed, the combobox input will show a placeholder string if no values are present. - Type: `string` - Required: No +#### isLoading + +Show a spinner (and hide the suggestions dropdown) while data about the matching suggestions (ie the `options` prop) is loading + +- Type: `Boolean` +- Required: No + #### __experimentalRenderItem Custom renderer invoked for each option in the suggestion list. The render prop receives as its argument an object containing, under the `item` key, the single option's data (directly from the array of data passed to the `options` prop). diff --git a/packages/components/src/combobox-control/index.tsx b/packages/components/src/combobox-control/index.tsx index 28510c8653d02e..9c6bd2a4d75dd2 100644 --- a/packages/components/src/combobox-control/index.tsx +++ b/packages/components/src/combobox-control/index.tsx @@ -35,6 +35,7 @@ import type { TokenInputProps } from '../form-token-field/types'; import { useDeprecated36pxDefaultSizeProp } from '../utils/use-deprecated-props'; import { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events'; import { maybeWarnDeprecated36pxSize } from '../utils/deprecated-36px-size'; +import Spinner from '../spinner'; const noop = () => {}; @@ -126,6 +127,7 @@ function ComboboxControl( props: ComboboxControlProps ) { help, allowReset = true, className, + isLoading = false, messages = { selected: __( 'Item selected.' ), }, @@ -362,6 +364,7 @@ function ComboboxControl( props: ComboboxControlProps ) { onChange={ onInputChange } /> + { isLoading && } { allowReset && (