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`] = ` " -
+

Cool cover

" diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index f3c8049d44b779..64a6dae10ec90f 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -562,6 +562,7 @@ function GalleryEdit( props ) { max={ Math.min( MAX_COLUMNS, images.length ) } { ...MOBILE_CONTROL_PROPS_RANGE_CONTROL } required + size="__unstable-large" /> ) } { hasLinkTo && ( ) } { Platform.isWeb && ! imageSizeOptions && hasImageIds && ( diff --git a/packages/block-library/src/group/deprecated.js b/packages/block-library/src/group/deprecated.js index dc523d9d512a32..68c01c7d3b16f7 100644 --- a/packages/block-library/src/group/deprecated.js +++ b/packages/block-library/src/group/deprecated.js @@ -50,7 +50,8 @@ const deprecated = [ default: 'div', }, templateLock: { - type: 'string', + type: [ 'string', 'boolean' ], + enum: [ 'all', 'insert', false ], }, }, supports: { diff --git a/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap index 61c8de02bd03a2..56a5fe3c259120 100644 --- a/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap +++ b/packages/block-library/src/image/test/__snapshots__/transforms.native.js.snap @@ -42,7 +42,7 @@ exports[`Image block transformations to Group block 1`] = ` exports[`Image block transformations to Media & Text block 1`] = ` " -
+

" diff --git a/packages/block-library/src/latest-comments/style.scss b/packages/block-library/src/latest-comments/style.scss index 604729f763caa0..8759da8c73d94d 100644 --- a/packages/block-library/src/latest-comments/style.scss +++ b/packages/block-library/src/latest-comments/style.scss @@ -1,7 +1,7 @@ // Lower specificity - target list element. ol.wp-block-latest-comments { // Removes left spacing in Customizer Widgets screen. - // Due to low specificity this will be safely overriden + // Due to low specificity this will be safely overridden // by default wp-block layout styles in the Post/Site editor margin-left: 0; diff --git a/packages/block-library/src/media-text/block.json b/packages/block-library/src/media-text/block.json index 1c9fc0b6820f1e..ded230a2eb1006 100644 --- a/packages/block-library/src/media-text/block.json +++ b/packages/block-library/src/media-text/block.json @@ -10,7 +10,7 @@ "attributes": { "align": { "type": "string", - "default": "wide" + "default": "none" }, "mediaAlt": { "type": "string", diff --git a/packages/block-library/src/media-text/deprecated.js b/packages/block-library/src/media-text/deprecated.js index 95595f8da6424b..2d061213dface8 100644 --- a/packages/block-library/src/media-text/deprecated.js +++ b/packages/block-library/src/media-text/deprecated.js @@ -13,6 +13,7 @@ import { useInnerBlocksProps, useBlockProps, } from '@wordpress/block-editor'; +import { compose } from '@wordpress/compose'; /** * Internal dependencies @@ -30,6 +31,19 @@ const v1ToV5ImageFillStyles = ( url, focalPoint ) => { : {}; }; +const v6ImageFillStyles = ( url, focalPoint ) => { + return url + ? { + backgroundImage: `url(${ url })`, + backgroundPosition: focalPoint + ? `${ Math.round( focalPoint.x * 100 ) }% ${ Math.round( + focalPoint.y * 100 + ) }%` + : `50% 50%`, + } + : {}; +}; + const DEFAULT_MEDIA_WIDTH = 50; const noop = () => {}; @@ -49,6 +63,20 @@ const migrateCustomColors = ( attributes ) => { }; }; +// After align attribute's default was updated this function explicitly sets +// the align value for deprecated blocks to the `wide` value which was default +// for their versions of this block. +const migrateDefaultAlign = ( attributes ) => { + if ( attributes.align ) { + return attributes; + } + + return { + ...attributes, + align: 'wide', + }; +}; + const baseAttributes = { align: { type: 'string', @@ -133,6 +161,40 @@ const v4ToV5BlockAttributes = { }, }; +const v6Attributes = { + ...v4ToV5BlockAttributes, + mediaAlt: { + type: 'string', + source: 'attribute', + selector: 'figure img', + attribute: 'alt', + default: '', + __experimentalRole: 'content', + }, + mediaId: { + type: 'number', + __experimentalRole: 'content', + }, + mediaUrl: { + type: 'string', + source: 'attribute', + selector: 'figure video,figure img', + attribute: 'src', + __experimentalRole: 'content', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'figure a', + attribute: 'href', + __experimentalRole: 'content', + }, + mediaType: { + type: 'string', + __experimentalRole: 'content', + }, +}; + const v4ToV5Supports = { anchor: true, align: [ 'wide', 'full' ], @@ -143,6 +205,166 @@ const v4ToV5Supports = { }, }; +const v6Supports = { + ...v4ToV5Supports, + color: { + gradients: true, + link: true, + __experimentalDefaultControls: { + background: true, + text: true, + }, + }, + spacing: { + margin: true, + padding: true, + }, + typography: { + fontSize: true, + lineHeight: true, + __experimentalFontFamily: true, + __experimentalFontWeight: true, + __experimentalFontStyle: true, + __experimentalTextTransform: true, + __experimentalTextDecoration: true, + __experimentalLetterSpacing: true, + __experimentalDefaultControls: { + fontSize: true, + }, + }, +}; + +// Version with wide as the default alignment. +// See: https://github.com/WordPress/gutenberg/pull/48404 +const v6 = { + attributes: v6Attributes, + supports: v6Supports, + save( { attributes } ) { + const { + isStackedOnMobile, + mediaAlt, + mediaPosition, + mediaType, + mediaUrl, + mediaWidth, + mediaId, + verticalAlignment, + imageFill, + focalPoint, + linkClass, + href, + linkTarget, + rel, + } = attributes; + const mediaSizeSlug = + attributes.mediaSizeSlug || DEFAULT_MEDIA_SIZE_SLUG; + const newRel = isEmpty( rel ) ? undefined : rel; + + const imageClasses = classnames( { + [ `wp-image-${ mediaId }` ]: mediaId && mediaType === 'image', + [ `size-${ mediaSizeSlug }` ]: mediaId && mediaType === 'image', + } ); + + let image = ( + { + ); + + if ( href ) { + image = ( + + { image } + + ); + } + + const mediaTypeRenders = { + image: () => image, + video: () =>