diff --git a/.eslintrc.js b/.eslintrc.js index b7962fddda881a..67671070aa2a71 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -46,11 +46,6 @@ const restrictedImports = [ name: 'lodash', message: 'Please use native functionality instead.', }, - { - name: 'reakit', - message: - 'Please use Reakit API through `@wordpress/components` instead.', - }, { name: '@ariakit/react', message: @@ -357,6 +352,26 @@ module.exports = { 'jsdoc/require-param': 'off', }, }, + { + files: [ 'packages/components/src/**' ], + excludedFiles: [ 'packages/components/src/utils/colors-values.js' ], + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: 'Literal[value=/--wp-admin-theme-/]', + message: + '--wp-admin-theme-* variables do not support component theming. Use variables from the COLORS object in packages/components/src/utils/colors-values.js instead.', + }, + { + selector: + 'TemplateElement[value.cooked=/--wp-admin-theme-/]', + message: + '--wp-admin-theme-* variables do not support component theming. Use variables from the COLORS object in packages/components/src/utils/colors-values.js instead.', + }, + ], + }, + }, { files: [ 'packages/components/src/**' ], excludedFiles: [ 'packages/components/src/**/@(test|stories)/**' ], @@ -386,5 +401,11 @@ module.exports = { ], }, }, + { + files: [ 'packages/interactivity*/src/**' ], + rules: { + 'react/react-in-jsx-scope': 'error', + }, + }, ], }; diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1f944fa38b4840..bdd97598763621 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -132,7 +132,7 @@ /packages/report-flaky-tests @kevin940726 # wp-env -/packages/env @noahtallen @ObliviousHarmony @t-hamano +/packages/env @ObliviousHarmony @t-hamano # PHP /lib @spacedmonkey diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 5163a343541ca7..43e65749baae9c 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -183,7 +183,7 @@ jobs: NO_CHECKS: 'true' - name: Upload artifact - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: gutenberg-plugin path: ./gutenberg.zip @@ -206,7 +206,7 @@ jobs: - name: Upload release notes artifact if: ${{ needs.bump-version.outputs.new_version }} - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: release-notes path: ./release-notes.txt @@ -270,12 +270,12 @@ jobs: run: echo "version=$(echo $VERSION | cut -d / -f 3 | sed 's/-rc./ RC/' )" >> $GITHUB_OUTPUT - name: Download Plugin Zip Artifact - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: gutenberg-plugin - name: Download Release Notes Artifact - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: release-notes diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index 362aec73cd6241..f4e470eb177f93 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -41,7 +41,7 @@ jobs: npx wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" - name: Archive debug artifacts (screenshots, HTML snapshots) - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: failures-artifacts @@ -49,7 +49,7 @@ jobs: if-no-files-found: ignore - name: Archive flaky tests report - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: flaky-tests-report @@ -92,7 +92,7 @@ jobs: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e:playwright -- --shard=${{ matrix.part }}/${{ matrix.totalParts }} - name: Archive debug artifacts (screenshots, traces) - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: failures-artifacts @@ -100,7 +100,7 @@ jobs: if-no-files-found: ignore - name: Archive flaky tests report - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: flaky-tests-report @@ -120,7 +120,7 @@ jobs: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - - uses: actions/download-artifact@v4.1.0 + - uses: actions/download-artifact@v4.1.1 id: download_artifact # Don't fail the job if there isn't any flaky tests report. continue-on-error: true diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml index 4ef163694b947a..093eb9a325e365 100644 --- a/.github/workflows/enforce-pr-labels.yml +++ b/.github/workflows/enforce-pr-labels.yml @@ -12,7 +12,7 @@ jobs: with: mode: exactly count: 1 - labels: '[Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Regression, [Type] Security, [Type] WP Core Ticket, Backport from WordPress Core' + labels: '[Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Technical Prototype, [Type] Performance, [Type] Project Management, [Type] Regression, [Type] Security, [Type] WP Core Ticket, Backport from WordPress Core' add_comment: true message: "**Warning: Type of PR label mismatch**\n\n To merge this PR, it requires {{ errorString }} {{ count }} label indicating the type of PR. Other labels are optional and not being checked here. \n- **Type-related labels to choose from**: {{ provided }}.\n- **Labels found**: {{ applied }}.\n\nRead more about [Type labels in Gutenberg](https://github.com/WordPress/gutenberg/labels?q=type). Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task." exit_type: failure diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index bc457758b8385b..ade7ecd3e9a060 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -9,4 +9,4 @@ jobs: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/wrapper-validation-action@v2 diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 12063c0eb7d496..bef55a431cea97 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -86,7 +86,7 @@ jobs: - name: Archive performance results if: success() - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: performance-results path: ${{ env.WP_ARTIFACTS_PATH }}/*.performance-results*.json @@ -100,7 +100,7 @@ jobs: ./bin/log-performance-results.js $CODEHEALTH_PROJECT_TOKEN trunk $GITHUB_SHA b61dde2e5ec29d98801e623de968bfb286c5be3f $COMMITTED_AT - name: Archive debug artifacts (screenshots, HTML snapshots) - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: failure() with: name: failures-artifacts diff --git a/.github/workflows/php-changes-detection.yml b/.github/workflows/php-changes-detection.yml index fc0ba83b95dbc4..6a13d4d014fc69 100644 --- a/.github/workflows/php-changes-detection.yml +++ b/.github/workflows/php-changes-detection.yml @@ -17,7 +17,7 @@ jobs: - name: Get changed PHP files id: changed-files-php - uses: tj-actions/changed-files@716b1e13042866565e00e85fd4ec490e186c4a2f # v41.0.1 + uses: tj-actions/changed-files@90a06d6ba9543371ab4df8eeca0be07ca6054959 # v42.0.2 with: files: | *.{php} @@ -38,7 +38,7 @@ jobs: echo "formatted_change_list=$formatted_change_list" >> $GITHUB_OUTPUT - name: Find Comment - uses: peter-evans/find-comment@v2 + uses: peter-evans/find-comment@v3 id: find-comment with: issue-number: ${{ github.event.pull_request.number }} @@ -47,7 +47,7 @@ jobs: - name: Create comment if: steps.find-comment.outputs.comment-id == '' && steps.changed-files-php.outputs.any_changed == 'true' - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.pull_request.number }} body: | @@ -67,7 +67,7 @@ jobs: - name: Update comment if: steps.find-comment.outputs.comment-id != '' && steps.changed-files-php.outputs.any_changed == 'true' - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} @@ -89,7 +89,7 @@ jobs: - name: Update comment if: steps.find-comment.outputs.comment-id != '' && steps.changed-files-php.outputs.any_changed != 'true' - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/props-bot.yml b/.github/workflows/props-bot.yml new file mode 100644 index 00000000000000..0f21f47ef14f99 --- /dev/null +++ b/.github/workflows/props-bot.yml @@ -0,0 +1,88 @@ +name: Props Bot + +on: + # This event runs anytime a PR is (re)opened, updated, marked ready for review, or labeled. + # GitHub does not allow filtering the `labeled` event by a specific label. + # However, the logic below will short-circuit the workflow when the `props-bot` label is not the one being added. + # Note: The pull_request_target event is used instead of pull_request because this workflow needs permission to comment + # on the pull request. Because this event grants extra permissions to `GITHUB_TOKEN`, any code changes within the PR + # should be considered untrusted. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/. + pull_request_target: + types: + - opened + - synchronize + - reopened + - labeled + - ready_for_review + # This event runs anytime a comment is added or deleted. + # You cannot filter this event for PR comments only. + # However, the logic below does short-circuit the workflow for issues. + issue_comment: + type: + - created + # This event will run everytime a new PR review is initially submitted. + pull_request_review: + types: + - submitted + # This event runs anytime a PR review comment is created or deleted. + pull_request_review_comment: + types: + - created + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for pull requests + # or the commit hash for any other events. + group: ${{ github.workflow }}-${{ contains( fromJSON( '["pull_request_target", "pull_request_review", "pull_request_review_comment"]' ), github.event_name ) && github.head_ref || github.sha }} + cancel-in-progress: true + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + # Compiles a list of props for a pull request. + # + # Performs the following steps: + # - Collects a list of contributor props and leaves a comment. + # - Removes the props-bot label, if necessary. + props-bot: + name: Generate a list of props + runs-on: ubuntu-latest + permissions: + # The action needs permission `write` permission for PRs in order to add a comment. + pull-requests: write + contents: read + timeout-minutes: 20 + # The job will run when pull requests are open, ready for review and: + # + # - A comment is added to the pull request. + # - A review is created or commented on. + # - The pull request is opened, synchronized, marked ready for review, or reopened. + # - The `props-bot` label is added to the pull request. + if: | + ( + github.event_name == 'issue_comment' && github.event.issue.pull_request || + contains( fromJSON( '["pull_request_review", "pull_request_review_comment"]' ), github.event_name ) || + github.event_name == 'pull_request_target' && github.event.action != 'labeled' || + 'props-bot' == github.event.label.name + ) && + ( ! github.event.pull_request.draft && github.event.pull_request.state == 'open' || ! github.event.issue.draft && github.event.issue.state == 'open' ) + + steps: + - name: Gather a list of contributors + uses: WordPress/props-bot-action@trunk + + - name: Remove the props-bot label + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + if: ${{ github.event.action == 'labeled' && 'props-bot' == github.event.label.name }} + with: + retries: 2 + retry-exempt-status-codes: 418 + script: | + github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: '${{ github.event.number }}', + name: 'props-bot' + }); diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml index d0b8778a1d3c7c..85c71199d28e14 100644 --- a/.github/workflows/pull-request-automation.yml +++ b/.github/workflows/pull-request-automation.yml @@ -24,7 +24,7 @@ jobs: check-latest: true - name: Cache NPM packages - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index e0a3b9639cf389..e40938eef8e586 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -37,7 +37,7 @@ jobs: uses: ./.github/setup-node - name: Restore tests setup cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: | ~/.appium @@ -47,10 +47,10 @@ jobs: run: npm run native test:e2e:setup - name: Gradle cache - uses: gradle/gradle-build-action@982da8e78c05368c70dac0351bb82647a9e9a5d2 # v2.11.1 + uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3.0.0 - name: AVD cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 id: avd-cache with: path: | @@ -60,7 +60,7 @@ jobs: - name: Create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@99a4aac18b4df9b3af66c4a1f04c1f23fa10c270 # v2.29.0 + uses: reactivecircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 # v2.30.1 with: api-level: ${{ matrix.api-level }} force-avd-creation: false @@ -71,7 +71,7 @@ jobs: script: echo "Generated AVD snapshot for caching." - name: Run tests - uses: reactivecircus/android-emulator-runner@99a4aac18b4df9b3af66c4a1f04c1f23fa10c270 # v2.29.0 + uses: reactivecircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 # v2.30.1 with: api-level: ${{ matrix.api-level }} force-avd-creation: false @@ -81,13 +81,13 @@ jobs: profile: Nexus 6 script: npm run native test:e2e:android:local ${{ matrix.native-test-name }} - - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: android-screen-recordings path: packages/react-native-editor/android-screen-recordings - - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: appium-logs diff --git a/.github/workflows/rnmobile-ios-runner.yml b/.github/workflows/rnmobile-ios-runner.yml index 9ead788343b8dc..220874d7649195 100644 --- a/.github/workflows/rnmobile-ios-runner.yml +++ b/.github/workflows/rnmobile-ios-runner.yml @@ -37,7 +37,7 @@ jobs: uses: ./.github/setup-node - name: Restore tests setup cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: | ~/.appium @@ -50,7 +50,7 @@ jobs: 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 - name: Restore build cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: | packages/react-native-editor/ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app @@ -58,7 +58,7 @@ jobs: key: ${{ runner.os }}-ios-build-${{ matrix.xcode }}-${{ matrix.device }}-${{ hashFiles('ios-checksums.txt') }} - name: Restore pods cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: | packages/react-native-editor/ios/Pods @@ -84,13 +84,13 @@ jobs: rm packages/react-native-editor/ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app/main.jsbundle rm -rf packages/react-native-editor/ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app/assets - - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: ios-screen-recordings path: packages/react-native-editor/ios-screen-recordings - - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: appium-logs diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index b6d5465ab43a6c..523418a79ef49a 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -125,7 +125,7 @@ jobs: # dependency versions are installed and cached. ## - name: Set up PHP - uses: shivammathur/setup-php@e6f75134d35752277f093989e72e140eaa222f35 # v2.28.0 + uses: shivammathur/setup-php@6d7209f44a25a59e904b1ee9f3b0c33ab2cd888d # v2.29.0 with: php-version: '${{ matrix.php }}' ini-file: development @@ -225,7 +225,7 @@ jobs: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Set up PHP - uses: shivammathur/setup-php@e6f75134d35752277f093989e72e140eaa222f35 # v2.28.0 + uses: shivammathur/setup-php@6d7209f44a25a59e904b1ee9f3b0c33ab2cd888d # v2.29.0 with: php-version: '7.4' coverage: none @@ -238,7 +238,7 @@ jobs: run: echo "date=$(/bin/date -u --date='last Mon' "+%F")" >> $GITHUB_OUTPUT - name: Cache PHPCS scan cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: .cache/phpcs.json key: ${{ runner.os }}-date-${{ steps.get-date.outputs.date }}-phpcs-cache-${{ hashFiles('**/composer.json', 'phpcs.xml.dist') }} diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index d6d5f8da1102bb..e71bade9d0cd87 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -147,7 +147,7 @@ jobs: fi - name: Upload Changelog artifact - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: changelog ${{ matrix.label }} path: ./changelog.txt @@ -189,7 +189,7 @@ jobs: sed -i "s/$STABLE_TAG_PLACEHOLDER/Stable tag: $VERSION/g" ./trunk/readme.txt - name: Download Changelog Artifact - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: changelog trunk path: trunk @@ -247,7 +247,7 @@ jobs: sed -i "s/$STABLE_TAG_PLACEHOLDER/Stable tag: $VERSION/g" "$VERSION/readme.txt" - name: Download Changelog Artifact - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: changelog trunk path: ${{ github.event.release.name }} diff --git a/bin/api-docs/gen-theme-reference.js b/bin/api-docs/gen-theme-reference.js index f638bb708890a8..0ea9e282e5463e 100644 --- a/bin/api-docs/gen-theme-reference.js +++ b/bin/api-docs/gen-theme-reference.js @@ -74,6 +74,42 @@ const keys = ( maybeObject ) => { return Object.keys( maybeObject ); }; +/** + * Get definition from ref. + * + * @param {string} ref + * @return {Object} definition + * @throws {Error} If the referenced definition is not found in 'themejson.definitions'. + * + * @example + * getDefinition( '#/definitions/typographyProperties/properties/fontFamily' ) + * // returns themejson.definitions.typographyProperties.properties.fontFamily + */ +const resolveDefinitionRef = ( ref ) => { + const refParts = ref.split( '/' ); + const definition = refParts[ refParts.length - 1 ]; + if ( ! themejson.definitions[ definition ] ) { + throw new Error( `Can't resolve '${ ref }'. Definition not found` ); + } + return themejson.definitions[ definition ]; +}; + +/** + * Get properties from an array. + * + * @param {Object} items + * @return {Object} properties + */ +const getPropertiesFromArray = ( items ) => { + // if its a $ref resolve it + if ( items.$ref ) { + return resolveDefinitionRef( items.$ref ).properties; + } + + // otherwise just return the properties + return items.properties; +}; + /** * Convert settings properties to markup. * @@ -96,7 +132,9 @@ const getSettingsPropertiesMarkup = ( struct ) => { const def = 'default' in props[ key ] ? props[ key ].default : ''; const ps = props[ key ].type === 'array' - ? keys( props[ key ].items.properties ).sort().join( ', ' ) + ? keys( getPropertiesFromArray( props[ key ].items ) ) + .sort() + .join( ', ' ) : ''; markup += `| ${ key } | ${ props[ key ].type } | ${ def } | ${ ps } |\n`; } ); diff --git a/bin/check-latest-npm.js b/bin/check-latest-npm.js deleted file mode 100644 index 8b9f86e95dd567..00000000000000 --- a/bin/check-latest-npm.js +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env node - -/** - * External dependencies - */ -const { green, red, yellow } = require( 'chalk' ); -const { get } = require( 'https' ); -const { spawn } = require( 'child_process' ); -const semver = require( 'semver' ); - -/** - * Internal dependencies - */ -const { - engines: { npm: npmRange }, - // Ignore reason: `package.json` exists outside `bin` `rootDir`. - // @ts-ignore -} = require( '../package.json' ); - -/** - * Returns a promise resolving with the version number of the latest available - * version of npm. - * - * @return {Promise} Promise resolving with latest npm version. - */ -async function getLatestNPMVersion() { - return new Promise( ( resolve, reject ) => { - get( - 'https://registry.npmjs.org/npm', - { - headers: { - // By passing a specialized `Accept` header, the registry - // will return an abbreviated form of the package data which - // includes enough detail to determine the latest version. - // - // See: https://github.com/npm/registry/blob/HEAD/docs/responses/package-metadata.md - Accept: 'application/vnd.npm.install-v1+json', - }, - }, - async ( response ) => { - if ( response.statusCode !== 200 ) { - return reject( - new Error( 'Package data for npm not found' ) - ); - } - - let body = ''; - for await ( const chunk of response ) { - body += chunk.toString(); - } - - let data; - try { - data = JSON.parse( body ); - } catch { - return reject( - new Error( - 'Package data for npm returned invalid response body' - ) - ); - } - - const versions = Object.values( data[ 'dist-tags' ] ); - - resolve( semver.maxSatisfying( versions, npmRange ) ); - } - ).on( 'error', ( error ) => { - if ( - /** @type {NodeJS.ErrnoException} */ ( error ).code === - 'ENOTFOUND' - ) { - error = - new Error( `Could not contact the npm registry to determine latest version. - -This could be due to an intermittent outage of the service, or because you are not connected to the internet. - -Because it is important that \`package-lock.json\` files only be committed while running the latest version of npm, this commit has been blocked. - -If you are certain of your changes and desire to commit anyways, you should either connect to the internet or bypass commit verification using ${ yellow( - 'git commit --no-verify' - ) } .` ); - } - - reject( error ); - } ); - } ); -} - -/** - * Returns a promise resolving with the version number of the local installed - * version of npm. - * - * @return {Promise} Promise resolving with local installed npm version. - */ -async function getLocalNPMVersion() { - return new Promise( async ( resolve ) => { - // 'npm' doesn't work correctly on Windows. - // https://github.com/WordPress/gutenberg/issues/22484 - const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; - const childProcess = spawn( command, [ '-v' ] ); - - let output = ''; - for await ( const chunk of childProcess.stdout ) { - output += chunk.toString(); - } - - resolve( output.trim() ); - } ); -} - -Promise.all( [ getLatestNPMVersion(), getLocalNPMVersion() ] ) - .then( ( [ latest, local ] ) => { - if ( latest !== local ) { - throw new Error( - `The local npm version does not match the expected latest version. Expected ${ green( - latest - ) }, found ${ red( local ) }. - -It is required that you have the expected latest version of npm installed in order to commit a change to the package-lock.json file. - -Run ${ yellow( - `npm install --global npm@${ latest }` - ) } to install the expected latest version of npm. Before retrying your commit, run ${ yellow( - 'npm install' - ) } once more to ensure the package-lock.json contents are correct. If there are any changes to the file, they should be included in your commit.` - ); - } - } ) - .catch( ( error ) => { - console.error( - 'Latest npm check failed!\n\n' + error.toString() + '\n' - ); - process.exitCode = 1; - } ); diff --git a/bin/cherry-pick.mjs b/bin/cherry-pick.mjs index 0c6a6c613638ba..dc71eed751cfdc 100644 --- a/bin/cherry-pick.mjs +++ b/bin/cherry-pick.mjs @@ -10,7 +10,7 @@ const LABEL = process.argv[ 2 ] || 'Backport to WP Beta/RC'; const BRANCH = getCurrentBranch(); const GITHUB_CLI_AVAILABLE = spawnSync( 'gh', [ 'auth', 'status' ] ) ?.stdout?.toString() - .includes( '✓ Logged in to github.com as' ); + .includes( '✓ Logged in to github.com' ); const AUTO_PROPAGATE_RESULTS_TO_GITHUB = GITHUB_CLI_AVAILABLE; @@ -114,16 +114,23 @@ async function fetchPRs() { const { items } = await GitHubFetch( `/search/issues?q=is:pr state:closed sort:updated label:"${ LABEL }" repo:WordPress/gutenberg` ); - const PRs = items.map( ( { id, number, title, pull_request, closed_at } ) => ( { - id, - number, - title, - pull_request, - } ) ) + const PRs = items + .map( ( { id, number, title, pull_request, closed_at } ) => ( { + id, + number, + title, + pull_request, + } ) ) .filter( ( { pull_request } ) => !! pull_request?.merged_at ) - .sort( ( a, b ) => new Date( a?.pull_request?.merged_at ) - new Date( b?.pull_request?.merged_at ) ); + .sort( + ( a, b ) => + new Date( a?.pull_request?.merged_at ) - + new Date( b?.pull_request?.merged_at ) + ); - console.log( 'Found the following PRs to cherry-pick (sorted by closed date in ascending order): ' ); + console.log( + 'Found the following PRs to cherry-pick (sorted by closed date in ascending order): ' + ); PRs.forEach( ( { number, title } ) => console.log( indent( `#${ number } – ${ title }` ) ) ); diff --git a/bin/generate-php-sync-issue.mjs b/bin/generate-php-sync-issue.mjs new file mode 100644 index 00000000000000..a45d77d354107c --- /dev/null +++ b/bin/generate-php-sync-issue.mjs @@ -0,0 +1,505 @@ +/** + * External dependencies + */ + +import Octokit from '@octokit/rest'; +import fs from 'fs'; + +import { fileURLToPath } from 'url'; +import nodePath, { dirname } from 'path'; + +function getArg( argName ) { + const arg = process.argv.find( ( _arg ) => + _arg.startsWith( `--${ argName }=` ) + ); + return arg ? arg.split( '=' )[ 1 ] : null; +} + +const OWNER = 'wordpress'; +const REPO = 'gutenberg'; +const MAX_MONTHS_TO_QUERY = 4; + +// The following paths will be ignored when generating the issue content. +const IGNORED_PATHS = [ + 'lib/load.php', // plugin specific code. + 'lib/experiments-page.php', // experiments are plugin specific. + 'packages/e2e-tests/plugins', // PHP files related to e2e tests only. + 'packages/block-library', // this is handled automatically. +]; + +// PRs containing the following labels will be ignored when generating the issue content. +const LABELS_TO_IGNORE = [ 'Backport from WordPress Core' ]; + +const MAX_NESTING_LEVEL = 3; + +const __filename = fileURLToPath( import.meta.url ); +const __dirname = dirname( __filename ); + +const authToken = getArg( 'token' ); +const stableWPRelease = getArg( 'wpstable' ); + +async function main() { + if ( ! authToken ) { + console.error( + 'Error. The --token argument is required. See: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token' + ); + process.exit( 1 ); + } + + if ( ! stableWPRelease ) { + console.error( + 'Error. The --wpstable argument is required. It should be the current stable WordPress release (e.g. 6.4).' + ); + process.exit( 1 ); + } + + const sinceArg = getArg( 'since' ); + let since; + + if ( sinceArg ) { + if ( validateDate( sinceArg ) ) { + since = sinceArg; + } else { + console.error( + `Error: The --since argument cannot be more than ${ MAX_MONTHS_TO_QUERY } months from the current date.` + ); + process.exit( 1 ); + } + } else { + console.error( + `Error. The --since argument is required (e.g. YYYY-MM-DD). This should be the date of the final Gutenberg release that was included in the last stable WP Core release (see https://developer.wordpress.org/block-editor/contributors/versions-in-wordpress/).` + ); + process.exit( 1 ); + } + + const lastRcDateArg = getArg( 'lastrcdate' ); + let lastRcDate; + + if ( lastRcDateArg ) { + if ( + validateDate( lastRcDateArg ) && + isAfter( lastRcDateArg, since ) + ) { + lastRcDate = lastRcDateArg; + } else { + console.error( + `Error: The --lastrcdate argument must be a date after the --since date.` + ); + process.exit( 1 ); + } + } else { + console.error( + `Error: The --lastrcdate argument is required (e.g. YYYY-MM-DD).` + ); + process.exit( 1 ); + } + + console.log( 'Welcome to the PHP Sync Issue Generator!' ); + + console.log( '--------------------------------' ); + console.log( '• Running script...' ); + + // These should be paths where we expect to find PHP files that + // will require syncing to WordPress Core. This list should be + // extremely selective. + const paths = [ '/lib', '/phpunit' ]; + + console.log( `• Fetching all commits made to ${ REPO } since: ${ since }` ); + let commits = await fetchAllCommitsFromPaths( since, paths ); + + // Remove identical commits based on sha + commits = commits.reduce( ( acc, current ) => { + const x = acc.find( ( item ) => item.sha === current.sha ); + if ( ! x ) { + return acc.concat( [ current ] ); + } + return acc; + }, [] ); + + // Fetch the full commit data for each of the commits. + // This is because the /commits endpoint does not include the + // information about the `files` modified in the commit. + console.log( + `• Fetching full commit data for ${ commits.length } commits` + ); + const commitsWithCommitData = await Promise.all( + commits.map( async ( commit ) => { + const commitData = await fetchCommit( commit.sha ); + + // In the future we will want to exclude PRs based on label + // so we will need to fetch the full PR data for each commit. + // For now we can just set this to null. + const fullPRData = null; + // const fullPRData = getPullRequestDataForCommit( commit.sha ); + + // Our Issue links to the PRs associated with the commits so we must + // provide this data. We could also get the PR data from the commit data, + // using getPullRequestDataForCommit, but that requires yet another + // network request. Therefore we optimise for trying to build + // the PR URL from the commit data we have available. + commitData.pullRequest = { + url: fullPRData?.html_url || buildPRURL( commit ), + creator: + fullPRData?.user?.login || + commit?.author?.login || + 'unknown', + labels: fullPRData?.labels || [], + }; + + // if the PR labels contain any of the labels to ignore, skip this commit + // by returning null. + if ( + commitData.pullRequest.labels.some( ( label ) => + LABELS_TO_IGNORE.includes( label.name ) + ) + ) { + return null; + } + + // This is temporarily required because PRs merged between Beta 1 (since) + // and the final RC may have already been manually backported to Core. + // This is however no reliable way to identify these PRs as the `Backport to WP beta/RC` + // label is manually removed once the PR has been backported. + // In future releases we will add a **new** label `Backported` + // to indicate the PR was backported to Core. + // As a result, in the future we will be able to exclude any PRs that have + // already been backported using the `Backported` label. + if ( isAfter( lastRcDate, commitData.commit.committer.date ) ) { + commitData.isBeforeLastRCDate = true; + } + + return commitData; + } ) + ); + + const processResult = pipe( + processCommits, + reduceNesting, + dedupePRsPerLevel, + removeEmptyLevels, + sortLevels + ); + + console.log( `• Processing ${ commitsWithCommitData.length } commits` ); + const result = processResult( commitsWithCommitData ); + + console.log( `• Generating Issue content` ); + const content = generateIssueContent( result ); + + // Write the Markdown content to a file + fs.writeFileSync( nodePath.join( __dirname, 'issueContent.md' ), content ); +} + +/** + * Checks if the first date is after the second date. + * + * @param {string} date1 - The first date. + * @param {string} date2 - The second date. + * @return {boolean} - Returns true if the first date is after the second date, false otherwise. + */ +function isAfter( date1, date2 ) { + return new Date( date1 ) > new Date( date2 ); +} + +function validateDate( sinceArg ) { + const sinceDate = new Date( sinceArg ); + const maxPreviousDate = new Date(); + maxPreviousDate.setMonth( + maxPreviousDate.getMonth() - MAX_MONTHS_TO_QUERY + ); + + return sinceDate >= maxPreviousDate; +} + +async function octokitPaginate( method, params ) { + return octokitRequest( method, params, { paginate: true } ); +} + +async function octokitRequest( method = '', params = {}, settings = {} ) { + const octokit = new Octokit( { auth: authToken } ); + params.owner = OWNER; + params.repo = REPO; + + const requestType = settings?.paginate ? 'paginate' : 'request'; + + try { + const result = await octokit[ requestType ]( method, params ); + + if ( requestType === 'paginate' ) { + return result; + } + return result.data; + } catch ( error ) { + console.error( + `Error making request to ${ method }: ${ error.message }` + ); + process.exit( 1 ); + } +} + +async function fetchAllCommitsFromPaths( since, paths ) { + let commits = []; + + for ( const path of paths ) { + const pathCommits = await fetchAllCommits( since, path ); + commits = [ ...commits, ...pathCommits ]; + } + + return commits; +} + +function buildPRURL( commit ) { + const prIdMatch = commit.commit.message.match( /\(#(\d+)\)/ ); + const prId = prIdMatch ? prIdMatch[ 1 ] : null; + return prId + ? `https://github.com/WordPress/gutenberg/pull/${ prId }` + : `[Commit](${ commit.html_url })`; +} + +function sortLevels( data ) { + function processLevel( levelData ) { + const processedData = {}; + + // Separate directories and files + const directories = {}; + const files = {}; + + for ( const [ key, value ] of Object.entries( levelData ) ) { + if ( key.endsWith( '.php' ) ) { + files[ key ] = Array.isArray( value ) + ? value + : processLevel( value ); + } else { + directories[ key ] = Array.isArray( value ) + ? value + : processLevel( value ); + } + } + + // Combine directories and files + Object.assign( processedData, directories, files ); + + return processedData; + } + + return processLevel( data ); +} + +function removeEmptyLevels( data ) { + function processLevel( levelData ) { + const processedData = {}; + + for ( const [ key, value ] of Object.entries( levelData ) ) { + if ( Array.isArray( value ) ) { + if ( value.length > 0 ) { + processedData[ key ] = value; + } + } else { + const processedLevel = processLevel( value ); + if ( Object.keys( processedLevel ).length > 0 ) { + processedData[ key ] = processedLevel; + } + } + } + + return processedData; + } + + return processLevel( data ); +} + +function dedupePRsPerLevel( data ) { + function processLevel( levelData ) { + const processedData = {}; + const prSet = new Set(); + + for ( const [ key, value ] of Object.entries( levelData ) ) { + if ( Array.isArray( value ) ) { + processedData[ key ] = value.filter( ( commit ) => { + if ( ! prSet.has( commit.pullRequest.url ) ) { + prSet.add( commit.pullRequest.url ); + return true; + } + return false; + } ); + } else { + processedData[ key ] = processLevel( value ); + } + } + + return processedData; + } + + return processLevel( data ); +} + +function reduceNesting( data ) { + function processLevel( levelData, level = 1 ) { + const processedData = {}; + + for ( const [ key, value ] of Object.entries( levelData ) ) { + if ( Array.isArray( value ) ) { + processedData[ key ] = value; + } else if ( level < MAX_NESTING_LEVEL ) { + processedData[ key ] = processLevel( value, level + 1 ); + } else { + processedData[ key ] = flattenData( value ); + } + } + + return processedData; + } + + function flattenData( nestedData ) { + let flatData = []; + + for ( const value of Object.values( nestedData ) ) { + if ( Array.isArray( value ) ) { + flatData = [ ...flatData, ...value ]; + } else { + flatData = [ ...flatData, ...flattenData( value ) ]; + } + } + + return flatData; + } + + return processLevel( data ); +} + +function processCommits( commits ) { + const result = {}; + + // This dir sholud be ignored, since whatever is in there is already in core. + // It exists to provide compatibility for older releases, because we have to + // support the current and the previous WP versions. + // See: https://github.com/WordPress/gutenberg/pull/57890#pullrequestreview-1828994247. + const prevReleaseCompatDirToIgnore = `lib/compat/wordpress-${ stableWPRelease }`; + + commits.forEach( ( commit ) => { + // Skip commits without an associated pull request + if ( ! commit?.pullRequest ) { + return; + } + commit.files.forEach( ( file ) => { + // Skip files that are not PHP files. + if ( ! file.filename.endsWith( '.php' ) ) { + return; + } + + if ( + [ ...IGNORED_PATHS, prevReleaseCompatDirToIgnore ].some( + ( path ) => + file.filename.startsWith( path ) || + file.filename === path + ) + ) { + // Skip files within specific packages. + return; + } + + const parts = file.filename.split( '/' ); + + let current = result; + + // If the file is under 'phpunit', always add it to the 'phpunit' key + // as it's helpful to have a full list of commits that modify tests. + if ( parts.includes( 'phpunit' ) ) { + current.phpunit = current.phpunit || []; + current.phpunit = [ ...current.phpunit, commit ]; + } + + for ( let i = 0; i < parts.length; i++ ) { + const part = parts[ i ]; + + if ( i === parts.length - 1 ) { + current[ part ] = current[ part ] || []; + current[ part ] = [ ...current[ part ], commit ]; + } else { + current[ part ] = current[ part ] || {}; + current = current[ part ]; + } + } + } ); + } ); + + return result; +} + +function formatPRLine( { pullRequest: pr, isBeforeLastRCDate } ) { + return `- [ ] ${ pr.url } - @${ + pr.creator + } | Trac ticket | Core backport PR ${ + isBeforeLastRCDate ? '(⚠️ Check for existing WP Core backport)' : '' + }\n`; +} + +function formatHeading( level, key ) { + const emoji = key.endsWith( '.php' ) ? '📄' : '📁'; + return `${ '#'.repeat( level ) } ${ emoji } ${ key }\n\n`; +} + +function generateIssueContent( result, level = 1 ) { + let issueContent = ''; + let isFirstSection = true; + + for ( const [ key, value ] of Object.entries( result ) ) { + // Add horizontal rule divider between sections, but not before the first section + if ( level <= 2 && ! isFirstSection ) { + issueContent += '\n---\n'; + } + + issueContent += formatHeading( level, key ); + + if ( Array.isArray( value ) ) { + value.forEach( ( commit ) => { + issueContent += formatPRLine( commit ); + } ); + } else { + issueContent += generateIssueContent( value, level + 1 ); + } + + isFirstSection = false; + } + + return issueContent; +} + +async function fetchAllCommits( since, path ) { + return await octokitPaginate( 'GET /repos/{owner}/{repo}/commits', { + since, + per_page: 30, + path, + } ); +} + +async function fetchCommit( sha ) { + return octokitRequest( 'GET /repos/{owner}/{repo}/commits/{sha}', { + sha, + } ); +} + +// eslint-disable-next-line no-unused-vars +async function getPullRequestDataForCommit( commitSha ) { + const pullRequests = octokitRequest( + 'GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls', + { + commit_sha: commitSha, + } + ); + + // If a related Pull Request is found, return its URL and creator + if ( pullRequests.length > 0 ) { + const pullRequest = pullRequests[ 0 ]; + return pullRequest; + } + + return null; +} + +const pipe = + ( ...fns ) => + ( x ) => + fns.reduce( ( v, f ) => f( v ), x ); + +main(); diff --git a/bin/list-experimental-api-matches.sh b/bin/list-experimental-api-matches.sh index 156464c4e7375e..d9399e63e5cf64 100755 --- a/bin/list-experimental-api-matches.sh +++ b/bin/list-experimental-api-matches.sh @@ -31,7 +31,7 @@ namespace() { awk -F: ' { print module($1), $2 } function module(path) { - n = split(path, parts, "/") + split(path, parts, "/") if (parts[1] == "lib") return "lib" return parts[1] "/" parts[2] }' diff --git a/bin/packages/build.js b/bin/packages/build.js index f4597d4f1425fa..61914bab5700a7 100755 --- a/bin/packages/build.js +++ b/bin/packages/build.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + /** * External dependencies */ diff --git a/bin/packages/check-build-type-declaration-files.js b/bin/packages/check-build-type-declaration-files.js index fff0b51e32fc24..74c7cfa5e3c2ac 100644 --- a/bin/packages/check-build-type-declaration-files.js +++ b/bin/packages/check-build-type-declaration-files.js @@ -69,13 +69,16 @@ async function getDecFile( packagePath ) { async function typecheckDeclarations( file ) { return new Promise( ( resolve, reject ) => { - exec( `npx tsc --noEmit ${ file }`, ( error, stdout, stderr ) => { - if ( error ) { - reject( { file, error, stderr, stdout } ); - } else { - resolve( { file, stdout } ); + exec( + `npx tsc --target esnext --moduleResolution node --noEmit ${ file }`, + ( error, stdout, stderr ) => { + if ( error ) { + reject( { file, error, stderr, stdout } ); + } else { + resolve( { file, stdout } ); + } } - } ); + ); } ); } diff --git a/bin/tsconfig.json b/bin/tsconfig.json index f3d576c178d0c0..48fd553e4c049b 100644 --- a/bin/tsconfig.json +++ b/bin/tsconfig.json @@ -17,7 +17,6 @@ }, "files": [ "./api-docs/update-api-docs.js", - "./check-latest-npm.js", "./plugin/config.js", "./plugin/commands/changelog.js", "./plugin/commands/performance.js", diff --git a/changelog.txt b/changelog.txt index de2e8d243ff740..e306af2e9de232 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,1265 @@ == Changelog == += 17.6.4 = + +### Bug Fixes + + - Compatibility with WordPress trunk: Do not register font collections if already registered by Core. + + += 17.6.3 = + +## Changelog + +### Bug Fixes + +#### Block Editor +- Navigation: Update the fallback block list to avoid a PHP Warning ([58588](https://github.com/WordPress/gutenberg/pull/58588)) + +## Contributors + +The following contributors merged PRs in this release: + +@dd32 + + += 17.6.2 = + + +## Changelog + +### Bug Fixes + +#### Block Editor +- Writing flow: Maintain correct selection after drag-selecting outside block. ([58684](https://github.com/WordPress/gutenberg/pull/58684)) +- useOnBlockDrop: Fix TypeError via array coercion. ([58686](https://github.com/WordPress/gutenberg/pull/58686)) + + + + +## Contributors + +The following contributors merged PRs in this release: + +@ellatrix @mcsf + + += 17.6.1 = + + +## Changelog + +### Font Library + +- Make the API compatible with the upcoming Core patch. ([58671](https://github.com/WordPress/gutenberg/pull/58671)) + + +## Contributors + +The following contributors merged PRs in this release: + +@youknowriad + + += 17.6.0 = + +## Changelog + +### Features + +#### Interactivity API +- Add `wp-data-on-window` and `wp-data-on-document` directives. ([57931](https://github.com/WordPress/gutenberg/pull/57931)) +- Add `wp-each` directive. ([57859](https://github.com/WordPress/gutenberg/pull/57859)) +- Add `wp-run` directive and `useInit` & `useWatch` hooks. ([57805](https://github.com/WordPress/gutenberg/pull/57805)) +- Create `@wordpress/interactivity-router` module. ([57924](https://github.com/WordPress/gutenberg/pull/57924)) + +#### Typography +- Add defaultFontSizes option to theme.json. ([56661](https://github.com/WordPress/gutenberg/pull/56661)) + +#### Font Library +- Font Library: Add wp_get_font_dir() function. ([57730](https://github.com/WordPress/gutenberg/pull/57730)) + +#### Custom Fields +- Block Bindings: Disable editing of bound block attributes in editor UI. ([58085](https://github.com/WordPress/gutenberg/pull/58085)) + +#### Block Editor +- Add effects/box shadow tools to block inspector. ([57654](https://github.com/WordPress/gutenberg/pull/57654)) + + +### Enhancements + +#### Components +- Add opt-in prop for 40px default size for `BoxControl`, `BorderControl`, and `BorderBoxControl`. ([56185](https://github.com/WordPress/gutenberg/pull/56185)) +- BorderControl: Replace style picker with ToggleGroupControl. ([57562](https://github.com/WordPress/gutenberg/pull/57562)) +- ColorPicker: Store internal HSLA state for better slider UX. ([57555](https://github.com/WordPress/gutenberg/pull/57555)) +- Migrate PaletteEdit and CircularOptionPicker tests from user-event to ariakit/test. ([57809](https://github.com/WordPress/gutenberg/pull/57809)) +- Replace `TabPanel` with `Tabs` in the Editor Preferences Modal. ([57293](https://github.com/WordPress/gutenberg/pull/57293)) +- Theme: Set `color` on wrapper. ([58095](https://github.com/WordPress/gutenberg/pull/58095)) +- Tooltip: No-op when nested inside another Tooltip component. ([57202](https://github.com/WordPress/gutenberg/pull/57202)) +- `BoxControl`: Update design. ([56665](https://github.com/WordPress/gutenberg/pull/56665)) +- Element: Start reexporting PureComponent. ([58076](https://github.com/WordPress/gutenberg/pull/58076)) + +#### Interactivity API +- Render the root interactive blocks. ([57729](https://github.com/WordPress/gutenberg/pull/57729)) +- Interactivity Router: Replace `data-wp-navigation-id` with `data-wp-router-region`. ([58191](https://github.com/WordPress/gutenberg/pull/58191)) +- Interactivity: Export `withScope()` and allow to use it with asynchronous operations. ([58013](https://github.com/WordPress/gutenberg/pull/58013)) +- Prevent the use of components in `wp-text`. ([57879](https://github.com/WordPress/gutenberg/pull/57879)) +- Remove wp-data-navigation-link directive. ([57853](https://github.com/WordPress/gutenberg/pull/57853)) +- Server Directive Processing Refactor. ([58066](https://github.com/WordPress/gutenberg/pull/58066)) +- Update `preact`, `@preact/signals` and `deepsignal` dependencies. ([57891](https://github.com/WordPress/gutenberg/pull/57891)) + +#### Block Editor +- Add copy link button to Link UI. ([58170](https://github.com/WordPress/gutenberg/pull/58170)) +- Improve LinkControl preview. ([57775](https://github.com/WordPress/gutenberg/pull/57775)) +- Keep Link UI open upon initial link creation when used in RichText. ([57726](https://github.com/WordPress/gutenberg/pull/57726)) +- List View: Displace list view items when dragging (a bit more WYSIWYG). ([56625](https://github.com/WordPress/gutenberg/pull/56625)) +- Show initial suggestions in rich text Link UI. ([57743](https://github.com/WordPress/gutenberg/pull/57743)) +- Disable lock button if user cannot control lock state. ([57274](https://github.com/WordPress/gutenberg/pull/57274)) +- Use ClipboardJS latest version and clean up focus loss workaround. ([57156](https://github.com/WordPress/gutenberg/pull/57156)) +- Dimensions: Add Aspect Ratio block support. ([56897](https://github.com/WordPress/gutenberg/pull/56897)) + +#### Block Library +- Add more taxonomy options to the post navigation link. ([48912](https://github.com/WordPress/gutenberg/pull/48912)) +- Add: Footnotes support for other CPT's. ([57353](https://github.com/WordPress/gutenberg/pull/57353)) +- Better navigation link variations for post types / taxonomies. ([56100](https://github.com/WordPress/gutenberg/pull/56100)) +- Remove "blocks" from copy and delete labels. ([57769](https://github.com/WordPress/gutenberg/pull/57769)) +- Pullquote Block: Add padding and margin support. ([45731](https://github.com/WordPress/gutenberg/pull/45731)) +- Video Block: Add raw transformation from `video` html. ([47159](https://github.com/WordPress/gutenberg/pull/47159)) +- Aspect ratio: Remove support on the Group block for now. ([58414](https://github.com/WordPress/gutenberg/pull/58414)) +- Image block: Move UI for lightbox from sidebar to the content toolbar alongside link settings. ([57608](https://github.com/WordPress/gutenberg/pull/57608)) + + +#### Data Views +- DataViews: Enable grid layout for templates & parts by default. ([58137](https://github.com/WordPress/gutenberg/pull/58137)) +- DataViews: Make dataviews powered page patterns stable. ([58139](https://github.com/WordPress/gutenberg/pull/58139)) +- DataViews: Make the "Manage Pages" stable. ([58166](https://github.com/WordPress/gutenberg/pull/58166)) +- Dataviews: Add Bulk actions to page. ([57826](https://github.com/WordPress/gutenberg/pull/57826)) + +#### Site Editor +- Group templates in sidebar list. ([57711](https://github.com/WordPress/gutenberg/pull/57711)) +- Initial routing refactoring to separate preview from list view. ([57938](https://github.com/WordPress/gutenberg/pull/57938)) +- Iterate on warning text for block removal for query/post template/post content. ([58138](https://github.com/WordPress/gutenberg/pull/58138)) +- Site editor: Add global styles changes to save flow. ([57470](https://github.com/WordPress/gutenberg/pull/57470)) +- Editor: Unify the Editor Mode preference. ([57642](https://github.com/WordPress/gutenberg/pull/57642)) +- Live Preview: Show the current theme name on the theme activation modal. ([57588](https://github.com/WordPress/gutenberg/pull/57588)) +- Unify the preferences modal UI between post and site editor. ([57639](https://github.com/WordPress/gutenberg/pull/57639)) +- Remove right negative margin from pinned items. ([57666](https://github.com/WordPress/gutenberg/pull/57666)) +- Update style revision top toolbar text. ([58057](https://github.com/WordPress/gutenberg/pull/58057)) + +#### Block API +- Block Bindings: Update source registration syntax and remove APIs that should be private. ([58205](https://github.com/WordPress/gutenberg/pull/58205)) +- Block Hooks: Do not remove toggle if hooked block is present elsewhere. ([57928](https://github.com/WordPress/gutenberg/pull/57928)) + +#### Synced Patterns +- Add basic pattern overrides end-to-end tests. ([57792](https://github.com/WordPress/gutenberg/pull/57792)) +- Use a patch format and support `linkTarget` of `core/button` for Pattern Overrides. ([58165](https://github.com/WordPress/gutenberg/pull/58165)) + +#### Patterns +- Add image block support for pattern overrides. ([57909](https://github.com/WordPress/gutenberg/pull/57909)) +- Outline editable blocks that are within a content-locked container. ([57901](https://github.com/WordPress/gutenberg/pull/57901)) +- Change text on pattern reset button. ([58286](https://github.com/WordPress/gutenberg/pull/58286)) + +#### Post Editor +- Post Lock: Use the new modal size preset. ([58197](https://github.com/WordPress/gutenberg/pull/58197)) +- Add description to the save panel header when nothing is checked. ([57716](https://github.com/WordPress/gutenberg/pull/57716)) + +#### Font Library +- Update the default collection data URL to the wordpress.org cdn. ([58186](https://github.com/WordPress/gutenberg/pull/58186)) +- Font Library: Refactor stylesheet using CSS variables ([58237](https://github.com/WordPress/gutenberg/pull/58237)) +- Font Library Modal: Reset the selected font when installing a new font. ([57817](https://github.com/WordPress/gutenberg/pull/57817)) +- Font Library: Disable font library UI using a PHP filter. ([57818](https://github.com/WordPress/gutenberg/pull/57818)) +- Font Library: Filter fonts upload directory. ([57697](https://github.com/WordPress/gutenberg/pull/57697)) +- Font Library: Use data or src file to define font collection data. ([57734](https://github.com/WordPress/gutenberg/pull/57734)) +- Improve font collection rest controller. ([58222](https://github.com/WordPress/gutenberg/pull/58222)) +- Make notices more consistent. ([58180](https://github.com/WordPress/gutenberg/pull/58180)) +- Updates Font Families and Font Faces endpoints context param. ([58287](https://github.com/WordPress/gutenberg/pull/58287)) + +#### Commands +- Minor command tweaks. ([58148](https://github.com/WordPress/gutenberg/pull/58148)) + +#### Extensibility +- Update Navigation block to render hooked inner blocks. ([57754](https://github.com/WordPress/gutenberg/pull/57754)) +- Add gettext content when translating 'Header'. ([51066](https://github.com/WordPress/gutenberg/pull/51066)) + +#### Template Editor +- Remove `template-only` mode from editor and edit-post packages. ([57700](https://github.com/WordPress/gutenberg/pull/57700)) + +### New APIs + +#### Block API +- Block Bindings API: Add block bindings PHP registration mechanisms and "Post meta" source under the experimental flag. ([57249](https://github.com/WordPress/gutenberg/pull/57249)) +- Block Bindings API: Refactor logic into Block Bindings class and singleton pattern. ([57742](https://github.com/WordPress/gutenberg/pull/57742)) + + +### Bug Fixes + +#### Block Library +- Avatar block: Fix broken aligments in the editor. ([58114](https://github.com/WordPress/gutenberg/pull/58114)) +- Embed Block: Fix retry processing when embedding with trailing slash fails. ([58007](https://github.com/WordPress/gutenberg/pull/58007)) +- Lightbox: Fix "Expand on click" control being disabled unintentionally. ([56053](https://github.com/WordPress/gutenberg/pull/56053)) +- Modified Date Block: Don't render change date tool. ([57914](https://github.com/WordPress/gutenberg/pull/57914)) +- Only prioritise Quote transform where relevant. ([57749](https://github.com/WordPress/gutenberg/pull/57749)) +- Query Loop: Fix posts list variation detection. ([58194](https://github.com/WordPress/gutenberg/pull/58194)) +- Block Hooks API: Update Navigation block feature gate. ([58388](https://github.com/WordPress/gutenberg/pull/58388)) + +#### Components +- Button: Always render the Tooltip component even when a tooltip should not be shown. ([56490](https://github.com/WordPress/gutenberg/pull/56490)) +- CustomSelect: Adjust `renderSelectedValue` to fix sizing. ([57865](https://github.com/WordPress/gutenberg/pull/57865)) +- ToggleGroupControl: Improve controlled value detection. ([57770](https://github.com/WordPress/gutenberg/pull/57770)) +- Tooltip: Accept specific tooltip props. ([58125](https://github.com/WordPress/gutenberg/pull/58125)) +- Tooltip: Forward and merge inner tooltip props correctly. ([57878](https://github.com/WordPress/gutenberg/pull/57878)) +- Bring back the chevron. ([57807](https://github.com/WordPress/gutenberg/pull/57807)) +- Preferences: Add a proxy to retrieve the deprecated preferences with a deprecation message. ([58016](https://github.com/WordPress/gutenberg/pull/58016)) + +#### Data Views +- DataViews: Default sort order in templates by title. ([58175](https://github.com/WordPress/gutenberg/pull/58175)) +- DataViews: Don't always display horizontal scrollbar. ([58101](https://github.com/WordPress/gutenberg/pull/58101)) +- DataViews: Fix author sorting in templates and template parts. ([58167](https://github.com/WordPress/gutenberg/pull/58167)) +- Remove unused argument from sprintf in pagination.js. ([57823](https://github.com/WordPress/gutenberg/pull/57823)) +- DataViews: Fix safari grid row height issue. ([58302](https://github.com/WordPress/gutenberg/pull/58302)) +- DataViews: Fix table view cell wrapper and BlockPreviews. ([58062](https://github.com/WordPress/gutenberg/pull/58062)) + +#### Patterns +- Add black border back when editing synced pattern in the post editor. ([57631](https://github.com/WordPress/gutenberg/pull/57631)) +- Outline editable blocks when in a pattern that has locked children. ([57991](https://github.com/WordPress/gutenberg/pull/57991)) +- Remove text align control for paragraph, heading, and button in contentOnly editing mode. ([57906](https://github.com/WordPress/gutenberg/pull/57906)) +- Pattern Categories: Fix capitalization. ([58112](https://github.com/WordPress/gutenberg/pull/58112)) +- Fix flaky "create a new pattern" test. ([57747](https://github.com/WordPress/gutenberg/pull/57747)) +- Block Bindings: Fix bindings image placeholder showing in patterns overrides. ([58252](https://github.com/WordPress/gutenberg/pull/58252)) + +#### List View +- Image Block: Make block name affect list view. ([57955](https://github.com/WordPress/gutenberg/pull/57955)) +- More Block: Make block name affect list view. ([58160](https://github.com/WordPress/gutenberg/pull/58160)) + +#### Block API +- Block Hooks: Fix toggle. ([57956](https://github.com/WordPress/gutenberg/pull/57956)) +- Fix formats not working in block bindings content. ([58055](https://github.com/WordPress/gutenberg/pull/58055)) + +#### Global Styles +- Correctly decode border color values. ([57876](https://github.com/WordPress/gutenberg/pull/57876)) +- Fix: Theme.json application of custom root selector for styles. ([58050](https://github.com/WordPress/gutenberg/pull/58050)) + +#### Data Layer +- Data: Allow binding registry selector to multiple registries. ([57943](https://github.com/WordPress/gutenberg/pull/57943)) +- Data: Fix memoized createRegistrySelector. ([57888](https://github.com/WordPress/gutenberg/pull/57888)) + +#### Typography +- #56734 When there is no font, the border should not appear. Display further guidance text. ([56825](https://github.com/WordPress/gutenberg/pull/56825)) +- Fluid typography: Do not calculate fluid font size when min and max viewport widths are equal. ([57866](https://github.com/WordPress/gutenberg/pull/57866)) + +#### Block Editor +- Fix regression: Content locking does not stops when an outside block is selected. ([57737](https://github.com/WordPress/gutenberg/pull/57737)) +- LinkControl: Remove unnecessary right padding of input fields. ([57784](https://github.com/WordPress/gutenberg/pull/57784)) + +#### Custom Fields +- Block Bindings: Fix button popover not showing in patterns. ([58219](https://github.com/WordPress/gutenberg/pull/58219)) + +#### Font Library +- Fix typo. ([58193](https://github.com/WordPress/gutenberg/pull/58193)) +- Removed
and