diff --git a/.github/setup-node/action.yml b/.github/setup-node/action.yml
new file mode 100644
index 00000000000000..22cb81618a1efb
--- /dev/null
+++ b/.github/setup-node/action.yml
@@ -0,0 +1,46 @@
+name: 'Setup Node.js and install npm dependencies'
+description: 'Configure Node.js and install npm dependencies while managing all aspects of caching.'
+inputs:
+ node-version:
+ description: 'Optional. The Node.js version to use. When not specified, the version specified in .nvmrc will be used.'
+ required: false
+ type: string
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Use desired version of Node.js
+ uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
+ with:
+ node-version-file: '.nvmrc'
+ node-version: ${{ inputs.node-version }}
+ cache: npm
+
+ - name: Get Node.js and npm version
+ id: node-version
+ run: |
+ echo "NODE_VERSION=$(node -v)" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - name: Cache node_modules
+ id: cache-node_modules
+ uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
+ with:
+ path: '**/node_modules'
+ key: node_modules-${{ runner.os }}-${{ steps.node-version.outputs.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
+
+ - name: Install npm dependencies
+ if: ${{ steps.cache-node_modules.outputs.cache-hit != 'true' }}
+ run: npm ci
+ shell: bash
+
+ # On cache hit, we run the post-install script to match the native `npm ci` behavior.
+ # An example of this is to patch `node_modules` using patch-package.
+ - name: Post-install
+ if: ${{ steps.cache-node_modules.outputs.cache-hit == 'true' }}
+ run: |
+ # Run the post-install script for the root project.
+ npm run postinstall
+ # Run the post-install scripts for workspaces.
+ npx lerna run postinstall
+ shell: bash
diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml
index 6e915d0ffddd27..9e30cc1c1cfc67 100644
--- a/.github/workflows/build-plugin-zip.yml
+++ b/.github/workflows/build-plugin-zip.yml
@@ -168,7 +168,7 @@ jobs:
with:
ref: ${{ needs.bump-version.outputs.release_branch || github.ref }}
- - name: Use desired version of NodeJS
+ - name: Use desired version of Node.js
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version-file: '.nvmrc'
@@ -326,7 +326,7 @@ jobs:
git config user.name "Gutenberg Repository Automation"
git config user.email gutenberg@wordpress.org
- - name: Setup Node (for CLI)
+ - name: Setup Node.js
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version-file: 'main/.nvmrc'
diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml
index 9ae0116ef1c79b..ea1facacdf236a 100644
--- a/.github/workflows/bundle-size.yml
+++ b/.github/workflows/bundle-size.yml
@@ -41,7 +41,7 @@ jobs:
with:
fetch-depth: 1
- - name: Use desired version of NodeJS
+ - name: Use desired version of Node.js
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version-file: '.nvmrc'
diff --git a/.github/workflows/create-block.yml b/.github/workflows/create-block.yml
index 304052d4297b87..d0eae662c90978 100644
--- a/.github/workflows/create-block.yml
+++ b/.github/workflows/create-block.yml
@@ -20,20 +20,17 @@ jobs:
strategy:
fail-fast: false
matrix:
- node: [14]
+ node: ['14']
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
with:
node-version: ${{ matrix.node }}
- cache: npm
- - name: npm install, build, format and lint
+ - name: Create block
shell: bash
- run: |
- npm ci
- bash ./bin/test-create-block.sh
+ run: bash ./bin/test-create-block.sh
diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml
index 24256e4266a46e..9bc1224fa11e7c 100644
--- a/.github/workflows/end2end-test.yml
+++ b/.github/workflows/end2end-test.yml
@@ -29,16 +29,11 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
- - name: Npm install and build
- run: |
- npm ci
- npm run build
+ - name: Npm build
+ run: npm run build
- name: Install WordPress
run: |
@@ -78,16 +73,11 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
- - name: Npm install and build
- run: |
- npm ci
- npm run build
+ - name: Npm build
+ run: npm run build
- name: Install Playwright dependencies
run: |
@@ -137,19 +127,14 @@ jobs:
name: flaky-tests-report
path: flaky-tests
- - name: Use desired version of NodeJS
+ - name: Setup Node.js and install dependencies
if: ${{ steps.download_artifact.outcome == 'success' }}
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
+ uses: ./.github/setup-node
- - name: Npm install and build
+ - name: Npm build
if: ${{ steps.download_artifact.outcome == 'success' }}
# TODO: We don't have to build the entire project, just the action itself.
- run: |
- npm ci
- npm run build:packages
+ run: npm run build:packages
- name: Report flaky tests
if: ${{ steps.download_artifact.outcome == 'success' }}
diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml
index ed2dac34ddc939..18f11e55b3de88 100644
--- a/.github/workflows/performance.yml
+++ b/.github/workflows/performance.yml
@@ -32,22 +32,8 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
-
- - name: Npm install
- run: |
- npm ci
-
- - name: Install specific versions of the themes used in tests
- run: |
- npm run wp-env start
- npm run wp-env -- run tests-cli "wp theme update twentytwentyone --version=1.7"
- npm run wp-env -- run tests-cli "wp theme update twentytwentythree --version=1.0"
- npm run wp-env stop
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
- name: Compare performance with trunk
if: github.event_name == 'pull_request'
diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml
index 6568d46989fe65..bc6782ae3ed8f9 100644
--- a/.github/workflows/publish-npm-packages.yml
+++ b/.github/workflows/publish-npm-packages.yml
@@ -49,7 +49,7 @@ jobs:
git config user.name "Gutenberg Repository Automation"
git config user.email gutenberg@wordpress.org
- - name: Setup Node (for CLI)
+ - name: Setup Node.js
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version-file: 'main/.nvmrc'
diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml
index 5d38f7965f7b10..b39a54f084c602 100644
--- a/.github/workflows/pull-request-automation.yml
+++ b/.github/workflows/pull-request-automation.yml
@@ -19,7 +19,7 @@ jobs:
with:
ref: trunk
- - name: Use desired version of NodeJS
+ - name: Use desired version of Node.js
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: ${{ matrix.node }}
diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml
index 0f1474c824a208..55d29accbcb8fb 100644
--- a/.github/workflows/rnmobile-android-runner.yml
+++ b/.github/workflows/rnmobile-android-runner.yml
@@ -31,13 +31,8 @@ jobs:
distribution: 'temurin'
java-version: '11'
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
-
- - run: npm ci
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
- name: Gradle cache
uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0
diff --git a/.github/workflows/rnmobile-ios-runner.yml b/.github/workflows/rnmobile-ios-runner.yml
index 933b4184957d11..1a4b1db788371b 100644
--- a/.github/workflows/rnmobile-ios-runner.yml
+++ b/.github/workflows/rnmobile-ios-runner.yml
@@ -25,13 +25,8 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
-
- - run: npm ci
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
- name: Prepare build cache key
run: find package-lock.json packages/react-native-editor/ios packages/react-native-aztec/ios packages/react-native-bridge/ios -type f -print0 | sort -z | xargs -0 shasum | tee ios-checksums.txt
diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml
index 00002aaf52f191..8935ef5e393258 100644
--- a/.github/workflows/static-checks.yml
+++ b/.github/workflows/static-checks.yml
@@ -24,7 +24,7 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
+ - name: Use desired version of Node.js
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version-file: '.nvmrc'
diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml
index 616fad4e55b5aa..ad49e3274f9ba8 100644
--- a/.github/workflows/storybook-pages.yml
+++ b/.github/workflows/storybook-pages.yml
@@ -16,14 +16,8 @@ jobs:
with:
ref: trunk
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
-
- - name: Install Dependencies
- run: npm ci
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
- name: Build Storybook
run: npm run storybook:build
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index ca9adb75ebca83..a78440bf5ef52e 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -33,19 +33,16 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
with:
node-version: ${{ matrix.node }}
- cache: npm
- - name: Npm install and build
+ - name: Npm build
# It's not necessary to run the full build, since Jest can interpret
# source files with `babel-jest`. Some packages have their own custom
# build tasks, however. These must be run.
- run: |
- npm ci
- npx lerna run build
+ run: npx lerna run build
- name: Running the tests
run: npm run test:unit -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache"
@@ -79,11 +76,8 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Set up Node.js
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
##
# This allows Composer dependencies to be installed using a single step.
@@ -116,10 +110,8 @@ jobs:
with:
custom-cache-suffix: $(/bin/date -u --date='last Mon' "+%F")
- - name: Install npm dependencies
- run: |
- npm ci
- npm run build
+ - name: Npm build
+ run: npm run build
- name: Docker debug information
run: |
@@ -235,19 +227,14 @@ jobs:
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- - name: Use desired version of NodeJS
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
- with:
- node-version-file: '.nvmrc'
- cache: npm
+ - name: Setup Node.js and install dependencies
+ uses: ./.github/setup-node
- - name: Npm install and build
+ - name: Npm build
# It's not necessary to run the full build, since Jest can interpret
# source files with `babel-jest`. Some packages have their own custom
# build tasks, however. These must be run.
- run: |
- npm ci
- npx lerna run build
+ run: npx lerna run build
- name: Running the tests
run: npm run native test -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache"
diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js
index 75ab95a4cc4a10..0d91947da80212 100644
--- a/bin/plugin/commands/performance.js
+++ b/bin/plugin/commands/performance.js
@@ -3,7 +3,7 @@
*/
const fs = require( 'fs' );
const path = require( 'path' );
-const { mapValues, kebabCase } = require( 'lodash' );
+const { mapValues } = require( 'lodash' );
const SimpleGit = require( 'simple-git' );
/**
@@ -83,6 +83,17 @@ const config = require( '../config' );
* @property {number=} maxListViewOpen Max time to open list view.
*/
+/**
+ * Sanitizes branch name to be used in a path or a filename.
+ *
+ * @param {string} branch
+ *
+ * @return {string} Sanitized branch name.
+ */
+function sanitizeBranchName( branch ) {
+ return branch.replace( /[^a-zA-Z0-9-]/g, '-' );
+}
+
/**
* Computes the average number from an array numbers.
*
@@ -299,8 +310,8 @@ async function runPerformanceTests( branches, options ) {
const branchDirectories = {};
for ( const branch of branches ) {
log( ` >> Branch: ${ branch }` );
- const environmentDirectory =
- rootDirectory + '/envs/' + kebabCase( branch );
+ const sanitizedBranch = sanitizeBranchName( branch );
+ const environmentDirectory = rootDirectory + '/envs/' + sanitizedBranch;
// @ts-ignore
branchDirectories[ branch ] = environmentDirectory;
const buildPath = `${ environmentDirectory }/plugin`;
@@ -401,7 +412,8 @@ async function runPerformanceTests( branches, options ) {
for ( let i = 0; i < TEST_ROUNDS; i++ ) {
rawResults[ i ] = {};
for ( const branch of branches ) {
- const runKey = `${ branch }_${ testSuite }_run-${ i }`;
+ const sanitizedBranch = sanitizeBranchName( branch );
+ const runKey = `${ sanitizedBranch }_${ testSuite }_run-${ i }`;
// @ts-ignore
const environmentDirectory = branchDirectories[ branch ];
log( ` >> Branch: ${ branch }, Suite: ${ testSuite }` );
diff --git a/bin/plugin/lib/utils.js b/bin/plugin/lib/utils.js
index 4a75437a60694e..c50094321710ca 100644
--- a/bin/plugin/lib/utils.js
+++ b/bin/plugin/lib/utils.js
@@ -33,6 +33,7 @@ function runShellScript( script, cwd, env = {} ) {
NO_CHECKS: 'true',
PATH: process.env.PATH,
HOME: process.env.HOME,
+ USER: process.env.USER,
...env,
},
},
diff --git a/bin/plugin/utils/.wp-env.performance.json b/bin/plugin/utils/.wp-env.performance.json
index 112c2684f64a48..3f80b794ddddd6 100644
--- a/bin/plugin/utils/.wp-env.performance.json
+++ b/bin/plugin/utils/.wp-env.performance.json
@@ -1,7 +1,11 @@
{
"core": "WordPress/WordPress",
"plugins": [ "./plugin" ],
- "themes": [ "../../tests/test/emptytheme" ],
+ "themes": [
+ "../../tests/test/emptytheme",
+ "https://downloads.wordpress.org/theme/twentytwentyone.1.7.zip",
+ "https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip"
+ ],
"env": {
"tests": {
"mappings": {
diff --git a/changelog.txt b/changelog.txt
index f5abba7bd8cb79..8c018ec7f9d05f 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -5477,7 +5477,7 @@ The following contributors merged PRs in this release:
- Docs: Fix required status of `onSelectUrl` prop of `MediaReplaceFlow` component. ([44025](https://github.com/WordPress/gutenberg/pull/44025))
- Document new global styles filters. ([44111](https://github.com/WordPress/gutenberg/pull/44111))
- Document template_lock=noContent for Custom Post Types. ([43977](https://github.com/WordPress/gutenberg/pull/43977))
-- InnerBlocks: document that `templateLock:NoContent` cannot be overriden by children. ([43825](https://github.com/WordPress/gutenberg/pull/43825))
+- InnerBlocks: document that `templateLock:NoContent` cannot be overridden by children. ([43825](https://github.com/WordPress/gutenberg/pull/43825))
- Theme.json: Add default values for settings.spacing.spacingScale. ([43860](https://github.com/WordPress/gutenberg/pull/43860))
- Theme.json: Fix schema for useRootPaddingAwareAlignments. ([43628](https://github.com/WordPress/gutenberg/pull/43628))
- Typography block supports: Call tear_down in tests and format PHP doc blocks. ([43968](https://github.com/WordPress/gutenberg/pull/43968))
diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md
index 8833b02d3b1244..efd4820b1adf55 100644
--- a/docs/how-to-guides/themes/theme-json.md
+++ b/docs/how-to-guides/themes/theme-json.md
@@ -1067,7 +1067,7 @@ h3 {
##### Element pseudo selectors
-Pseudo selectors `:hover`, `:focus`, `:visited` are supported by Gutenberg.
+Pseudo selectors `:hover`, `:focus`, `:visited`, `:active`, `:link`, `:any-link` are supported by Gutenberg.
```json
"elements": {
diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 1dee5a7509d7b1..8e4312704f50a2 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -59,7 +59,7 @@ Prompt visitors to take action with a group of button-style links. ([Source](htt
- **Name:** core/buttons
- **Category:** design
-- **Supports:** align (full, wide), anchor, spacing (blockGap, margin), typography (fontSize, lineHeight)
+- **Supports:** align (full, wide), anchor, spacing (blockGap, margin), typography (fontSize, lineHeight), ~~html~~
- **Attributes:**
## Calendar
@@ -105,7 +105,7 @@ Display content in multiple columns, with blocks added to each column. ([Source]
- **Name:** core/columns
- **Category:** design
- **Supports:** align (full, wide), anchor, color (background, gradients, link, text), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
-- **Attributes:** isStackedOnMobile, verticalAlignment
+- **Attributes:** isStackedOnMobile, templateLock, verticalAlignment
## Comment Author Avatar (deprecated)
diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md
index c7bb892103e004..2342fc0656221f 100644
--- a/docs/reference-guides/theme-json-reference/theme-json-living.md
+++ b/docs/reference-guides/theme-json-reference/theme-json-living.md
@@ -60,6 +60,7 @@ Settings related to shadows.
| Property | Type | Default | Props |
| --- | --- | --- |--- |
+| defaultPresets | boolean | true | |
| presets | array | | name, shadow, slug |
---
diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php
index ef950f80ce9448..b51fec1c5c99fb 100644
--- a/lib/block-supports/duotone.php
+++ b/lib/block-supports/duotone.php
@@ -426,128 +426,12 @@ function gutenberg_register_duotone_support( $block_type ) {
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
+ * @deprecated 6.3.0 Use WP_Duotone_Gutenberg::render_duotone_support() instead.
* @return string Filtered block content.
*/
function gutenberg_render_duotone_support( $block_content, $block ) {
- $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
-
- $duotone_support = false;
- if ( $block_type && property_exists( $block_type, 'supports' ) ) {
- $duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
- }
-
- $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );
-
- if (
- ! $duotone_support ||
- ! $has_duotone_attribute
- ) {
- return $block_content;
- }
-
- // Possible values for duotone attribute:
- // 1. Array of colors - e.g. array('#000000', '#ffffff').
- // 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue'.
- // 3. A CSS string - e.g. 'unset' to remove globally applied duotone.
- $duotone_attr = $block['attrs']['style']['color']['duotone'];
-
- $is_preset = is_string( $duotone_attr ) && strpos( $duotone_attr, 'var:preset|duotone|' ) === 0;
- $is_css = is_string( $duotone_attr ) && strpos( $duotone_attr, 'var:preset|duotone|' ) === false;
- $is_custom = is_array( $duotone_attr );
-
- // Generate the pieces needed for rendering a duotone to the page.
- if ( $is_preset ) {
- // Extract the slug from the preset variable string.
- $slug = str_replace( 'var:preset|duotone|', '', $duotone_attr );
-
- // Utilize existing preset CSS custom property.
- $filter_property = "var(--wp--preset--duotone--$slug)";
- } elseif ( $is_css ) {
- // Build a unique slug for the filter based on the CSS value.
- $slug = wp_unique_id( sanitize_key( $duotone_attr . '-' ) );
-
- // Pass through the CSS value.
- $filter_property = $duotone_attr;
- } elseif ( $is_custom ) {
- // Build a unique slug for the filter based on the array of colors.
- $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) );
-
- // This has the same shape as a preset, so it can be used in place of a
- // preset when getting the filter property and SVG filter.
- $filter_data = array(
- 'slug' => $slug,
- 'colors' => $duotone_attr,
- );
-
- // Build a customized CSS filter property for unique slug.
- $filter_property = gutenberg_get_duotone_filter_property( $filter_data );
-
- // SVG will be output on the page later.
- $filter_svg = gutenberg_get_duotone_filter_svg( $filter_data );
- }
-
- // - Applied as a class attribute to the block wrapper.
- // - Used as a selector to apply the filter to the block.
- $filter_id = gutenberg_get_duotone_filter_id( array( 'slug' => $slug ) );
-
- // Build the CSS selectors to which the filter will be applied.
- $selector = WP_Theme_JSON_Gutenberg::scope_selector( '.' . $filter_id, $duotone_support );
-
- // Calling gutenberg_style_engine_get_stylesheet_from_css_rules ensures that
- // the styles are rendered in an inline for block supports because we're
- // using the `context` option to instruct it so.
- gutenberg_style_engine_get_stylesheet_from_css_rules(
- array(
- array(
- 'selector' => $selector,
- 'declarations' => array(
- // !important is needed because these styles
- // render before global styles,
- // and they should be overriding the duotone
- // filters set by global styles.
- 'filter' => $filter_property . ' !important',
- ),
- ),
- ),
- array(
- 'context' => 'block-supports',
- )
- );
-
- // If we needed to generate an SVG, output it on the page.
- if ( isset( $filter_svg ) ) {
- add_action(
- 'wp_footer',
- static function () use ( $filter_svg, $selector ) {
- echo $filter_svg;
-
- /*
- * Safari renders elements incorrectly on first paint when the
- * SVG filter comes after the content that it is filtering, so
- * we force a repaint with a WebKit hack which solves the issue.
- */
- global $is_safari;
- if ( $is_safari ) {
- /*
- * Simply accessing el.offsetHeight flushes layout and style
- * changes in WebKit without having to wait for setTimeout.
- */
- printf(
- '',
- wp_json_encode( $selector )
- );
- }
- }
- );
- }
-
- // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
- return preg_replace(
- '/' . preg_quote( 'class="', '/' ) . '/',
- 'class="' . $filter_id . ' ',
- $block_content,
- 1
- );
+ _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Duotone_Gutenberg::render_duotone_support' );
+ return WP_Duotone_Gutenberg::render_duotone_support( $block_content, $block );
}
// Register the block support.
@@ -558,6 +442,10 @@ static function () use ( $filter_svg, $selector ) {
)
);
+add_action( 'wp_loaded', array( 'WP_Duotone_Gutenberg', 'set_global_styles_presets' ), 10 );
+add_action( 'wp_loaded', array( 'WP_Duotone_Gutenberg', 'set_global_style_block_names' ), 10 );
// Remove WordPress core filter to avoid rendering duplicate support elements.
remove_filter( 'render_block', 'wp_render_duotone_support', 10, 2 );
-add_filter( 'render_block', 'gutenberg_render_duotone_support', 10, 2 );
+add_filter( 'render_block', array( 'WP_Duotone_Gutenberg', 'render_duotone_support' ), 10, 2 );
+add_action( 'wp_enqueue_scripts', array( 'WP_Duotone_Gutenberg', 'output_global_styles' ), 11 );
+add_action( 'wp_footer', array( 'WP_Duotone_Gutenberg', 'output_footer_assets' ), 10 );
diff --git a/lib/class-wp-duotone-gutenberg.php b/lib/class-wp-duotone-gutenberg.php
new file mode 100644
index 00000000000000..3b3342db9f6170
--- /dev/null
+++ b/lib/class-wp-duotone-gutenberg.php
@@ -0,0 +1,348 @@
+
+ * [
+ * 'slug' => 'blue-orange',
+ * 'colors' => [ '#0000ff', '#ffcc00' ],
+ * ]
+ * ],
+ * …
+ * ]
+ *
+ * @since 6.3.0
+ * @var array
+ */
+ private static $global_styles_presets = array();
+
+ /**
+ * An array of block names from global, theme, and custom styles that have duotone presets. We'll use this to quickly
+ * check if a block being rendered needs to have duotone applied, and which duotone preset to use.
+ *
+ * Example:
+ * [
+ * 'core/featured-image' => 'blue-orange',
+ * …
+ * ]
+ *
+ * @since 6.3.0
+ * @var array
+ */
+ private static $global_styles_block_names = array();
+
+ /**
+ * An array of Duotone SVG and CSS output needed for the frontend duotone rendering based on what is
+ * being output on the page. Organized by a slug of the preset/color group and the information needed
+ * to generate the SVG and CSS at render.
+ *
+ * Example:
+ * [
+ * 'blue-orange' => [
+ * 'slug' => 'blue-orange',
+ * 'colors' => [ '#0000ff', '#ffcc00' ],
+ * ],
+ * 'wp-duotone-000000-ffffff-2' => [
+ * 'slug' => 'wp-duotone-000000-ffffff-2',
+ * 'colors' => [ '#000000', '#ffffff' ],
+ * ],
+ * ]
+ *
+ * @since 6.3.0
+ * @var array
+ */
+ private static $output = array();
+
+ /**
+ * Prefix used for generating and referencing duotone CSS custom properties.
+ */
+ const CSS_VAR_PREFIX = '--wp--preset--duotone--';
+
+ /**
+ * Get all possible duotone presets from global and theme styles and store as slug => [ colors array ]
+ * We only want to process this one time. On block render we'll access and output only the needed presets for that page.
+ */
+ public static function set_global_styles_presets() {
+ // Get the per block settings from the theme.json.
+ $tree = gutenberg_get_global_settings();
+ $presets_by_origin = _wp_array_get( $tree, array( 'color', 'duotone' ), array() );
+
+ foreach ( $presets_by_origin as $presets ) {
+ foreach ( $presets as $preset ) {
+ self::$global_styles_presets[ _wp_to_kebab_case( $preset['slug'] ) ] = array(
+ 'slug' => $preset['slug'],
+ 'colors' => $preset['colors'],
+ );
+ }
+ }
+ }
+
+ /**
+ * Scrape all block names from global styles and store in self::$global_styles_block_names
+ */
+ public static function set_global_style_block_names() {
+ // Get the per block settings from the theme.json.
+ $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
+ $block_nodes = $tree->get_styles_block_nodes();
+ $theme_json = $tree->get_raw_data();
+
+ foreach ( $block_nodes as $block_node ) {
+ // This block definition doesn't include any duotone settings. Skip it.
+ if ( empty( $block_node['duotone'] ) ) {
+ continue;
+ }
+
+ // Value looks like this: 'var(--wp--preset--duotone--blue-orange)' or 'var:preset|duotone|default-filter'.
+ $duotone_attr_path = array_merge( $block_node['path'], array( 'filter', 'duotone' ) );
+ $duotone_attr = _wp_array_get( $theme_json, $duotone_attr_path, array() );
+
+ if ( empty( $duotone_attr ) ) {
+ continue;
+ }
+ // If it has a duotone filter preset, save the block name and the preset slug.
+ $slug = self::gutenberg_get_slug_from_attr( $duotone_attr );
+
+ if ( $slug && $slug !== $duotone_attr ) {
+ self::$global_styles_block_names[ $block_node['name'] ] = $slug;
+ }
+ }
+ }
+
+ /**
+ * Take the inline CSS duotone variable from a block and return the slug. Handles styles slugs like:
+ * var:preset|duotone|default-filter
+ * var(--wp--preset--duotone--blue-orange)
+ *
+ * @param string $duotone_attr The duotone attribute from a block.
+ * @return string The slug of the duotone preset or an empty string if no slug is found.
+ */
+ private static function gutenberg_get_slug_from_attr( $duotone_attr ) {
+ // Uses Branch Reset Groups `(?|…)` to return one capture group.
+ preg_match( '/(?|var:preset\|duotone\|(\S+)|var\(--wp--preset--duotone--(\S+)\))/', $duotone_attr, $matches );
+
+ return ! empty( $matches[1] ) ? $matches[1] : '';
+ }
+
+ /**
+ * Check if we have a valid duotone preset.
+ *
+ * @param string $duotone_attr The duotone attribute from a block.
+ * @return bool True if the duotone preset present and valid.
+ */
+ private static function is_preset( $duotone_attr ) {
+ $slug = self::gutenberg_get_slug_from_attr( $duotone_attr );
+
+ return array_key_exists( $slug, self::$global_styles_presets );
+ }
+
+ /**
+ * Get the CSS variable name for a duotone preset.
+ *
+ * @param string $slug The slug of the duotone preset.
+ * @return string The CSS variable name.
+ */
+ private static function get_css_custom_property_name( $slug ) {
+ return self::CSS_VAR_PREFIX . $slug;
+ }
+
+ /**
+ * Get the CSS variable for a duotone preset.
+ *
+ * @param string $slug The slug of the duotone preset.
+ * @return string The CSS variable.
+ */
+ private static function get_css_var( $slug ) {
+ return 'var(' . self::get_css_custom_property_name( $slug ) . ')';
+ }
+
+ /**
+ * Get the CSS declaration for a duotone preset.
+ * Example: --wp--preset--duotone--blue-orange: url('#wp-duotone-blue-orange');
+ *
+ * @param array $filter_data The duotone data for presets and custom filters.
+ * @return string The CSS declaration.
+ */
+ private static function get_css_custom_property_declaration( $filter_data ) {
+ $declaration_value = gutenberg_get_duotone_filter_property( $filter_data );
+ $duotone_preset_css_property_name = self::get_css_custom_property_name( $filter_data['slug'] );
+ return $duotone_preset_css_property_name . ': ' . $declaration_value . ';';
+ }
+
+ /**
+ * Outputs all necessary SVG for duotone filters, CSS for classic themes.
+ */
+ public static function output_footer_assets() {
+ foreach ( self::$output as $filter_data ) {
+
+ // SVG will be output on the page later.
+ $filter_svg = gutenberg_get_duotone_filter_svg( $filter_data );
+
+ echo $filter_svg;
+
+ // This is for classic themes - in block themes, the CSS is added in the head via wp_add_inline_style in the wp_enqueue_scripts action.
+ if ( ! wp_is_block_theme() ) {
+ wp_add_inline_style( 'core-block-supports', 'body{' . self::get_css_custom_property_declaration( $filter_data ) . '}' );
+ }
+ }
+ }
+
+ /**
+ * Appends the used global style duotone filter CSS Vars to the inline global styles CSS
+ */
+ public static function output_global_styles() {
+
+ if ( empty( self::$output ) ) {
+ return;
+ }
+
+ $duotone_css_vars = '';
+
+ foreach ( self::$output as $filter_data ) {
+ if ( ! array_key_exists( $filter_data['slug'], self::$global_styles_presets ) ) {
+ continue;
+ }
+
+ $duotone_css_vars .= self::get_css_custom_property_declaration( $filter_data );
+ }
+
+ if ( ! empty( $duotone_css_vars ) ) {
+ wp_add_inline_style( 'global-styles', 'body{' . $duotone_css_vars . '}' );
+ }
+ }
+
+ /**
+ * Render out the duotone CSS styles and SVG.
+ *
+ * @param string $block_content Rendered block content.
+ * @param array $block Block object.
+ * @return string Filtered block content.
+ */
+ public static function render_duotone_support( $block_content, $block ) {
+ $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
+
+ $duotone_support = false;
+ if ( $block_type && property_exists( $block_type, 'supports' ) ) {
+ $duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
+ }
+
+ // The block should have a duotone attribute or have duotone defined in its theme.json to be processed.
+ $has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );
+ $has_global_styles_duotone = array_key_exists( $block['blockName'], self::$global_styles_block_names );
+
+ if (
+ empty( $block_content ) ||
+ ! $duotone_support ||
+ ( ! $has_duotone_attribute && ! $has_global_styles_duotone )
+ ) {
+ return $block_content;
+ }
+
+ // Generate the pieces needed for rendering a duotone to the page.
+ if ( $has_duotone_attribute ) {
+
+ // Possible values for duotone attribute:
+ // 1. Array of colors - e.g. array('#000000', '#ffffff').
+ // 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue' or 'var(--wp--preset--duotone--green-blue)''
+ // 3. A CSS string - e.g. 'unset' to remove globally applied duotone.
+
+ $duotone_attr = $block['attrs']['style']['color']['duotone'];
+ $is_preset = is_string( $duotone_attr ) && self::is_preset( $duotone_attr );
+ $is_css = is_string( $duotone_attr ) && ! $is_preset;
+ $is_custom = is_array( $duotone_attr );
+
+ if ( $is_preset ) {
+
+ // Extract the slug from the preset variable string.
+ $slug = self::gutenberg_get_slug_from_attr( $duotone_attr );
+
+ // Utilize existing preset CSS custom property.
+ $declaration_value = self::get_css_var( $slug );
+
+ self::$output[ $slug ] = self::$global_styles_presets[ $slug ];
+
+ } elseif ( $is_css ) {
+ // Build a unique slug for the filter based on the CSS value.
+ $slug = wp_unique_id( sanitize_key( $duotone_attr . '-' ) );
+
+ // Pass through the CSS value.
+ $declaration_value = $duotone_attr;
+ } elseif ( $is_custom ) {
+ // Build a unique slug for the filter based on the array of colors.
+ $slug = wp_unique_id( sanitize_key( implode( '-', $duotone_attr ) . '-' ) );
+
+ $filter_data = array(
+ 'slug' => $slug,
+ 'colors' => $duotone_attr,
+ );
+ // Build a customized CSS filter property for unique slug.
+ $declaration_value = gutenberg_get_duotone_filter_property( $filter_data );
+
+ self::$output[ $slug ] = $filter_data;
+ }
+ } elseif ( $has_global_styles_duotone ) {
+ $slug = self::$global_styles_block_names[ $block['blockName'] ];
+
+ // Utilize existing preset CSS custom property.
+ $declaration_value = self::get_css_var( $slug );
+
+ self::$output[ $slug ] = self::$global_styles_presets[ $slug ];
+ }
+
+ // - Applied as a class attribute to the block wrapper.
+ // - Used as a selector to apply the filter to the block.
+ $filter_id = gutenberg_get_duotone_filter_id( array( 'slug' => $slug ) );
+
+ // Build the CSS selectors to which the filter will be applied.
+ $selector = WP_Theme_JSON_Gutenberg::scope_selector( '.' . $filter_id, $duotone_support );
+
+ // We only want to add the selector if we have it in the output already, essentially skipping 'unset'.
+ if ( array_key_exists( $slug, self::$output ) ) {
+ self::$output[ $slug ]['selector'] = $selector;
+ }
+
+ // Pass styles to the block-supports stylesheet via the style engine.
+ // This ensures that Duotone styles are included in a single stylesheet,
+ // avoiding multiple style tags or multiple stylesheets being output to
+ // the site frontend.
+ gutenberg_style_engine_get_stylesheet_from_css_rules(
+ array(
+ array(
+ 'selector' => $selector,
+ 'declarations' => array(
+ // !important is needed because these styles
+ // render before global styles,
+ // and they should be overriding the duotone
+ // filters set by global styles.
+ 'filter' => $declaration_value . ' !important',
+ ),
+ ),
+ ),
+ array(
+ 'context' => 'block-supports',
+ )
+ );
+
+ // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
+ return preg_replace(
+ '/' . preg_quote( 'class="', '/' ) . '/',
+ 'class="' . $filter_id . ' ',
+ $block_content,
+ 1
+ );
+ }
+}
diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php
index 673e97d2b84e5c..5bd06274e6efd2 100644
--- a/lib/class-wp-theme-json-gutenberg.php
+++ b/lib/class-wp-theme-json-gutenberg.php
@@ -81,8 +81,8 @@ class WP_Theme_JSON_Gutenberg {
* - prevent_override => Disables override of default presets by theme presets.
* The relationship between whether to override the defaults
* and whether the defaults are enabled is inverse:
- * - If defaults are enabled => theme presets should not be overriden
- * - If defaults are disabled => theme presets should be overriden
+ * - If defaults are enabled => theme presets should not be overridden
+ * - If defaults are disabled => theme presets should be overridden
* For example, a theme sets defaultPalette to false,
* making the default palette hidden from the user.
* In that case, we want all the theme presets to be present,
@@ -144,8 +144,8 @@ class WP_Theme_JSON_Gutenberg {
'path' => array( 'color', 'duotone' ),
'prevent_override' => array( 'color', 'defaultDuotone' ),
'use_default_names' => false,
- 'value_func' => 'gutenberg_get_duotone_filter_property',
- 'css_vars' => '--wp--preset--duotone--$slug',
+ 'value_func' => null, // CSS Custom Properties for duotone are handled by block supports in class-wp-duotone-gutenberg.php.
+ 'css_vars' => null,
'classes' => array(),
'properties' => array( 'filter' ),
),
diff --git a/lib/compat/wordpress-6.1/blocks.php b/lib/compat/wordpress-6.1/blocks.php
index 8ee0acba6b6cfe..908cf3f088eb95 100644
--- a/lib/compat/wordpress-6.1/blocks.php
+++ b/lib/compat/wordpress-6.1/blocks.php
@@ -16,6 +16,8 @@
function gutenberg_safe_style_attrs_6_1( $attrs ) {
$attrs[] = 'flex-wrap';
$attrs[] = 'gap';
+ $attrs[] = 'column-gap';
+ $attrs[] = 'row-gap';
$attrs[] = 'margin-block-start';
$attrs[] = 'margin-block-end';
$attrs[] = 'margin-inline-start';
diff --git a/lib/compat/wordpress-6.1/template-parts-screen.php b/lib/compat/wordpress-6.1/template-parts-screen.php
index 1555b6ab1e250b..7c370635fe2ba0 100644
--- a/lib/compat/wordpress-6.1/template-parts-screen.php
+++ b/lib/compat/wordpress-6.1/template-parts-screen.php
@@ -133,7 +133,7 @@ static function( $classes ) {
}
}
- $active_global_styles_id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
+ $active_global_styles_id = WP_Theme_JSON_Resolver_Gutenberg::get_user_global_styles_post_id();
$active_theme = get_stylesheet();
$preload_paths = array(
array( '/wp/v2/media', 'OPTIONS' ),
diff --git a/lib/compat/wordpress-6.2/block-patterns.php b/lib/compat/wordpress-6.2/block-patterns.php
index 9a61041ee291b4..266f177d554470 100644
--- a/lib/compat/wordpress-6.2/block-patterns.php
+++ b/lib/compat/wordpress-6.2/block-patterns.php
@@ -433,7 +433,7 @@ function gutenberg_register_remote_theme_patterns() {
return;
}
- $pattern_settings = WP_Theme_JSON_Resolver::get_theme_data()->get_patterns();
+ $pattern_settings = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_patterns();
if ( empty( $pattern_settings ) ) {
return;
}
diff --git a/lib/compat/wordpress-6.3/rest-api.php b/lib/compat/wordpress-6.3/rest-api.php
index 93422289100dfa..f8f5fc91fed3d3 100644
--- a/lib/compat/wordpress-6.3/rest-api.php
+++ b/lib/compat/wordpress-6.3/rest-api.php
@@ -22,7 +22,7 @@ function gutenberg_register_rest_pattern_directory() {
* @param string $post_type Post type key.
*/
function gutenberg_update_templates_template_parts_rest_controller( $args, $post_type ) {
- if ( in_array( $post_type, array( 'wp_template', 'wp_template-part' ), true ) ) {
+ if ( in_array( $post_type, array( 'wp_template', 'wp_template_part' ), true ) ) {
$args['rest_controller_class'] = 'Gutenberg_REST_Templates_Controller_6_3';
}
return $args;
diff --git a/lib/compat/wordpress-6.3/script-loader.php b/lib/compat/wordpress-6.3/script-loader.php
new file mode 100644
index 00000000000000..8a8e41ef2be919
--- /dev/null
+++ b/lib/compat/wordpress-6.3/script-loader.php
@@ -0,0 +1,15 @@
+ div,
diff --git a/packages/block-editor/src/components/global-styles/utils.js b/packages/block-editor/src/components/global-styles/utils.js
index e8ca27d4b97e4d..2ff2e732298044 100644
--- a/packages/block-editor/src/components/global-styles/utils.js
+++ b/packages/block-editor/src/components/global-styles/utils.js
@@ -16,6 +16,7 @@ export const ROOT_BLOCK_SUPPORTS = [
'backgroundColor',
'color',
'linkColor',
+ 'captionColor',
'buttonColor',
'fontFamily',
'fontSize',
@@ -103,6 +104,7 @@ export const STYLE_PATH_TO_CSS_VAR_INFIX = {
'elements.link.typography.fontSize': 'font-size',
'elements.button.color.text': 'color',
'elements.button.color.background': 'color',
+ 'elements.caption.color.text': 'color',
'elements.button.typography.fontFamily': 'font-family',
'elements.button.typography.fontSize': 'font-size',
'elements.heading.color': 'color',
diff --git a/packages/block-editor/src/components/image-size-control/index.js b/packages/block-editor/src/components/image-size-control/index.js
index 151b05f88968ee..a547732d6f7bc1 100644
--- a/packages/block-editor/src/components/image-size-control/index.js
+++ b/packages/block-editor/src/components/image-size-control/index.js
@@ -43,6 +43,7 @@ export default function ImageSizeControl( {
options={ imageSizeOptions }
onChange={ onChangeImage }
help={ imageSizeHelp }
+ size="__unstable-large"
/>
) }
{ isResizable && (
@@ -58,6 +59,7 @@ export default function ImageSizeControl( {
onChange={ ( value ) =>
updateDimension( 'width', value )
}
+ size="__unstable-large"
/>
updateDimension( 'height', value )
}
+ size="__unstable-large"
/>
diff --git a/packages/block-editor/src/components/inner-blocks/README.md b/packages/block-editor/src/components/inner-blocks/README.md
index ce4eacb5e09954..eb42da998f0d0c 100644
--- a/packages/block-editor/src/components/inner-blocks/README.md
+++ b/packages/block-editor/src/components/inner-blocks/README.md
@@ -130,7 +130,7 @@ _Options:_
- `'insert'` — prevents inserting or removing blocks, but allows moving existing ones.
- `false` — prevents locking from being applied to an `InnerBlocks` area even if a parent block contains locking. ( Boolean )
-If locking is not set in an `InnerBlocks` area: the locking of the parent `InnerBlocks` area is used. Note that `contentOnly` can't be overriden: it's present, the `templateLock` value of any children is ignored.
+If locking is not set in an `InnerBlocks` area: the locking of the parent `InnerBlocks` area is used. Note that `contentOnly` can't be overridden: it's present, the `templateLock` value of any children is ignored.
If the block is a top level block: the locking of the Custom Post Type is used.
diff --git a/packages/block-editor/src/components/line-height-control/index.js b/packages/block-editor/src/components/line-height-control/index.js
index 670d6fac37e8df..61e0c4a3f32893 100644
--- a/packages/block-editor/src/components/line-height-control/index.js
+++ b/packages/block-editor/src/components/line-height-control/index.js
@@ -84,6 +84,15 @@ const LineHeightControl = ( {
? undefined
: { marginBottom: 24 };
+ const handleOnChange = ( nextValue, { event } ) => {
+ if ( event.type === 'click' ) {
+ onChange( adjustNextValue( nextValue, false ) );
+ return;
+ }
+
+ onChange( nextValue );
+ };
+
return (
type === 'core/link'
- );
-
- const linkFormatAtEnd = formats[ value.end - 1 ]?.find(
- ( { type } ) => type === 'core/link'
- );
-
- if (
- ! linkFormatAtStart ||
- ! linkFormatAtEnd ||
- linkFormatAtStart !== linkFormatAtEnd
- ) {
- isActive = false;
- }
- }
-
return (
{
@@ -84,6 +84,7 @@ function ColumnsEditContainer( {
allowedBlocks: ALLOWED_BLOCKS,
orientation: 'horizontal',
renderAppender: false,
+ templateLock,
} );
return (
diff --git a/packages/block-library/src/comments/index.php b/packages/block-library/src/comments/index.php
index a5e51cfbf4e793..807c0c3ead2b8f 100644
--- a/packages/block-library/src/comments/index.php
+++ b/packages/block-library/src/comments/index.php
@@ -212,6 +212,7 @@ function register_legacy_post_comments_block() {
* like `_wp_multiple_block_styles`, which is required in this case because
* the block has multiple styles.
*/
+ /** This filter is documented in wp-includes/blocks.php */
$metadata = apply_filters( 'block_type_metadata', $metadata );
register_block_type( 'core/post-comments', $metadata );
diff --git a/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap
index 252edf24172231..dc4ba0fbb2b1f8 100644
--- a/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap
+++ b/packages/block-library/src/cover/test/__snapshots__/transforms.native.js.snap
@@ -30,7 +30,7 @@ exports[`Cover block transformations with Image to Image block 1`] = `
exports[`Cover block transformations with Image to Media & Text block 1`] = `
"
-
+
Cool cover
"
@@ -60,7 +60,7 @@ exports[`Cover block transformations with Video to Group block 1`] = `
exports[`Cover block transformations with Video to Media & Text block 1`] = `
"
-
+ );
+ },
+ migrate: migrateDefaultAlign,
+ isEligible( attributes, innerBlocks, { block } ) {
+ const { attributes: finalizedAttributes } = block;
+ // When the align attribute defaults to none, valid block markup should
+ // not contain any alignment CSS class. Unfortunately, this
+ // deprecation's version of the block won't be invalidated due to the
+ // alignwide class still being in the markup. That is because the custom
+ // CSS classname support picks it up and adds it to the className
+ // attribute. At the time of parsing, the className attribute won't
+ // contain the alignwide class, hence the need to check the finalized
+ // block attributes.
+ return (
+ attributes.align === undefined &&
+ !! finalizedAttributes.className?.includes( 'alignwide' )
+ );
+ },
+};
+
+// Version with non-rounded background position attribute for focal point.
+// See: https://github.com/WordPress/gutenberg/pull/33915
const v5 = {
attributes: v4ToV5BlockAttributes,
supports: v4ToV5Supports,
@@ -252,9 +474,11 @@ const v5 = {
@@ -22,7 +22,7 @@ exports[`Media & Text block transformations with Image to Cover block 1`] = `
exports[`Media & Text block transformations with Image to Group block 1`] = `
"
-
+
Mountain
@@ -39,7 +39,7 @@ exports[`Media & Text block transformations with Image to Image block 1`] = `
exports[`Media & Text block transformations with Video to Columns block 1`] = `
"
-
+
Cloudup
@@ -58,7 +58,7 @@ exports[`Media & Text block transformations with Video to Cover block 1`] = `
exports[`Media & Text block transformations with Video to Group block 1`] = `
"
-