diff --git a/.eslintrc.js b/.eslintrc.js index 9933b831e4725a..bb262a70cf7c6b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -129,6 +129,7 @@ const restrictedImports = [ 'reject', 'repeat', 'reverse', + 'set', 'setWith', 'size', 'snakeCase', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 37d1abb5256f2a..7ba9e884259ea4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -54,6 +54,9 @@ # Full Site Editing /packages/edit-site +# Interactivity API +/packages/interactivity @luisherranz @darerodz + # Tooling /bin @ntwb @nerrad @ajitbohra /bin/api-docs @ntwb @nerrad @ajitbohra diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 9052f1689c9216..f57ccd21e361e8 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -56,7 +56,7 @@ jobs: github.event.inputs.version == 'rc' || github.event.inputs.version == 'stable' ) || ( - endsWith( github.ref, needs.compute-stable-branches.outputs.current_stable_branch ) && + startsWith( github.ref, 'refs/heads/release/' ) && github.event.inputs.version == 'stable' ) ) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 9c93b6b93d98dc..09494f0d514ad4 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -85,7 +85,6 @@ jobs: fail-fast: true matrix: php: - - '5.6' - '7.0' - '7.1' - '7.2' @@ -297,4 +296,4 @@ jobs: run: npx lerna run build - name: Running the tests - run: npm run native test -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" + run: npm run test:native -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 392cf912e8db54..521fd3a786f15e 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -5,10 +5,66 @@ on: types: [published] jobs: + compute-should-update-trunk: + name: Decide if trunk or tag + runs-on: ubuntu-latest + # Skip this job if the release is a release candidate. This will in turn skip + # the upload jobs, which are only relevant for non-RC releases. + # We first check if the release is a prerelease, and then if the ref contains + # the string "rc". The latter is fallback in case the deployer accidentally + # unchecks the "This is a pre-release" checkbox in the release UI. + if: | + !github.event.release.prerelease && !contains(github.ref, 'rc') + + outputs: + should_update_trunk: ${{ steps.compute_should_update_trunk.outputs.should_update_trunk }} + + steps: + - name: Fetch latest version in the WP core repo + id: compute_latest_version_in_core_repo + run: | + latest_version_in_core_repo=$(curl -s 'https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request\[slug\]=gutenberg' | jq -r '.version') + echo "Latest Core Repo version: $latest_version_in_core_repo" + echo "version=$latest_version_in_core_repo" >> $GITHUB_OUTPUT + + - name: Decide if it is a trunk or tag update + id: compute_should_update_trunk + env: + GITHUB_REF: ${{ github.ref }} + run: | + latestPublishedVersion=$(echo "$GITHUB_REF" | sed -E 's/refs\/tags\/(v?)([0-9.]+)/\2/') + latestVersionInCoreRepo="${{ steps.compute_latest_version_in_core_repo.outputs.version }}" + + # Determines if the first version string is greater than the second version string. + # + # Params: + # $1 - The first version string to compare, which may have an optional leading "v". + # $2 - The second version string to compare, which may have an optional leading "v". + # + # Return values: + # 0 - The first version string is greater than the second version string. + # 1 - The first version string is less than or equal to the second version string. + is_first_version_greater_than_second() { + v1=${1#v} + v2=${2#v} + dpkg --compare-versions "$v1" gt "$v2" + return $? + } + + # Only update trunk *if* the published release's version in Github is GREATER + # than the version currently published in the WP plugins repo. If not, then it + # will upload it as a new tag. + shouldUpdateTrunk=false + if is_first_version_greater_than_second "$latestPublishedVersion" "$latestVersionInCoreRepo"; then + shouldUpdateTrunk=true + fi + + echo "Should update trunk: $shouldUpdateTrunk" + echo "should_update_trunk=$shouldUpdateTrunk" >> $GITHUB_OUTPUT + get-release-branch: name: Get release branch name runs-on: ubuntu-latest - if: github.event.release.assets[0] outputs: release_branch: ${{ steps.get_release_branch.outputs.release_branch }} @@ -25,7 +81,8 @@ jobs: update-changelog: name: Update Changelog on ${{ matrix.branch }} branch runs-on: ubuntu-latest - if: github.event.release.assets[0] + if: | + github.event.release.assets[0] needs: get-release-branch env: TAG: ${{ github.event.release.tag_name }} @@ -95,11 +152,12 @@ jobs: path: ./changelog.txt upload: - name: Upload Gutenberg Plugin + name: Publish as trunk (and tag) runs-on: ubuntu-latest environment: wp.org plugin - needs: update-changelog - if: ${{ !github.event.release.prerelease && github.event.release.assets[0] }} + needs: [compute-should-update-trunk, update-changelog] + if: | + needs.compute-should-update-trunk.outputs.should_update_trunk == 'true' && github.event.release.assets[0] env: PLUGIN_REPO_URL: 'https://plugins.svn.wordpress.org/gutenberg' STABLE_VERSION_REGEX: '[0-9]\+\.[0-9]\+\.[0-9]\+\s*' @@ -109,11 +167,7 @@ jobs: steps: - name: Check out Gutenberg trunk from WP.org plugin repo - run: svn checkout "$PLUGIN_REPO_URL/trunk" - - - name: Get previous stable version - id: get_previous_stable_version - run: echo "stable_version=$(awk -F ':\ ' '$1 == "Stable tag" {print $2}' ./trunk/readme.txt)" >> $GITHUB_OUTPUT + run: svn checkout "$PLUGIN_REPO_URL/trunk" --username "$SVN_USERNAME" --password "$SVN_PASSWORD" - name: Delete everything working-directory: ./trunk @@ -130,8 +184,8 @@ jobs: - name: Replace the stable tag placeholder with the existing stable tag on the SVN repository env: STABLE_TAG_PLACEHOLDER: 'Stable tag: V\.V\.V' - STABLE_TAG: 'Stable tag: ${{ steps.get_previous_stable_version.outputs.stable_version }}' - run: sed -i "s/${STABLE_TAG_PLACEHOLDER}/${STABLE_TAG}/g" ./trunk/readme.txt + run: | + sed -i "s/$STABLE_TAG_PLACEHOLDER/Stable tag: $VERSION/g" ./trunk/readme.txt - name: Download Changelog Artifact uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1 @@ -159,3 +213,44 @@ jobs: sed -i "s/Stable tag: ${STABLE_VERSION_REGEX}/Stable tag: ${VERSION}/g" ./readme.txt svn commit -m "Releasing version $VERSION" \ --no-auth-cache --non-interactive --username "$SVN_USERNAME" --password "$SVN_PASSWORD" + + upload-tag: + name: Publish as tag + runs-on: ubuntu-latest + environment: wp.org plugin + needs: [compute-should-update-trunk, update-changelog] + if: | + needs.compute-should-update-trunk.outputs.should_update_trunk == 'false' && github.event.release.assets[0] + env: + PLUGIN_REPO_URL: 'https://plugins.svn.wordpress.org/gutenberg' + STABLE_VERSION_REGEX: '[0-9]\+\.[0-9]\+\.[0-9]\+\s*' + SVN_USERNAME: ${{ secrets.svn_username }} + SVN_PASSWORD: ${{ secrets.svn_password }} + VERSION: ${{ github.event.release.name }} + + steps: + - name: Download and unzip Gutenberg plugin asset into tags folder + env: + PLUGIN_URL: ${{ github.event.release.assets[0].browser_download_url }} + run: | + # do the magic here + curl -L -o gutenberg.zip $PLUGIN_URL + unzip gutenberg.zip -d "$VERSION" + rm gutenberg.zip + + - name: Replace the stable tag placeholder with the existing stable tag on the SVN repository + env: + STABLE_TAG_PLACEHOLDER: 'Stable tag: V\.V\.V' + run: | + sed -i "s/$STABLE_TAG_PLACEHOLDER/Stable tag: $VERSION/g" "$VERSION/readme.txt" + + - name: Download Changelog Artifact + uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # v3.0.1 + with: + name: changelog trunk + path: ${{ github.event.release.name }} + + - name: Add the new version directory and commit changes to the SVN repository + run: | + svn import "$VERSION" "$PLUGIN_REPO_URL/tags/$VERSION" -m "Committing version $VERSION" \ + --no-auth-cache --non-interactive --username "$SVN_USERNAME" --password "$SVN_PASSWORD" diff --git a/.gitignore b/.gitignore index 19e43aecea7b82..b44eabe00cccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ yarn.lock Thumbs.db # Report generated from jest-junit -test/native/junit.xml +junit.xml # Local overrides .wp-env.override.json diff --git a/changelog.txt b/changelog.txt index 277ac5f08ffb82..2904601f889553 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,384 @@ == Changelog == += 16.2.0-rc.3 = + +## Changelog + +### Bug Fixes + +#### Build Tooling +- Revert phpcs testVersion back to PHP 5.6. ([52384](https://github.com/WordPress/gutenberg/pull/52384)) + + += 16.2.0-rc.2 = + +## Changelog + +### Bug Fixes + +#### Block Library +- RichText/Footnotes: Make getRichTextValues work with InnerBlocks.Content. ([52241](https://github.com/WordPress/gutenberg/pull/52241)) + +#### Blocks +- Post and Comment Template blocks: Change render_block_context priority to 1 (https://github.com/WordPress/gutenberg/pull/52364) +- Footnotes: Fix incorrect anchor position in Firefox (https://github.com/WordPress/gutenberg/pull/52425) +- Footnotes: fix lingering format boundary attr (https://github.com/WordPress/gutenberg/pull/52439) +- Footnotes: save numbering through the entity provider (https://github.com/WordPress/gutenberg/pull/52423) + +#### Code Quality / Performance +- Iframe: avoid asset parsing & fix script localisation + +## Contributors + +The following contributors merged PRs in this release: + +@ellatrix @ockham @t-hamano + + += 16.1.2 = + +### Bug fixes + +* Footnotes: increase selector specificity for anchor (https://github.com/WordPress/gutenberg/pull/52179) + + += 16.2.0-rc.1 = + + + +## Changelog + +### Features + +#### Patterns +- feat: Rename Reusable blocks to Patterns. ([51704](https://github.com/WordPress/gutenberg/pull/51704)) + + +### Enhancements + +- Export store for the core/customize-widgets package. ([52189](https://github.com/WordPress/gutenberg/pull/52189)) + +#### Patterns +- Library: Add sync status to pattern details screen. ([51954](https://github.com/WordPress/gutenberg/pull/51954)) +- Rename Library to Patterns. ([52102](https://github.com/WordPress/gutenberg/pull/52102)) +- Update custom patterns label to 'My patterns'. ([51949](https://github.com/WordPress/gutenberg/pull/51949)) +- Update pattern creation modal in library. ([51946](https://github.com/WordPress/gutenberg/pull/51946)) +- Update template part icons in the library mosaic (grid items). ([51963](https://github.com/WordPress/gutenberg/pull/51963)) + +#### Site Editor +- Change "Home" template name to "Blog home". ([52048](https://github.com/WordPress/gutenberg/pull/52048)) +- Edit Site: Make loading spinner colors consistent. ([51857](https://github.com/WordPress/gutenberg/pull/51857)) +- Update the icon used to reference the blog. ([52075](https://github.com/WordPress/gutenberg/pull/52075)) + +#### Interactivity API +- Image block: Remove extra lookup for external image dimensions in lightbox. ([52178](https://github.com/WordPress/gutenberg/pull/52178)) +- Image block: Use built-in directive for mouseover event in lightbox. ([52067](https://github.com/WordPress/gutenberg/pull/52067)) + +#### Block Library +- Force full height for editor in Navigation focus mode. ([51798](https://github.com/WordPress/gutenberg/pull/51798)) +- Social links: Updating class and style attributes. ([51997](https://github.com/WordPress/gutenberg/pull/51997)) + +#### Themes +- Add border theme_support. ([51777](https://github.com/WordPress/gutenberg/pull/51777)) +- Add link color theme_support. ([51775](https://github.com/WordPress/gutenberg/pull/51775)) + +#### Global Styles +- Style Book: Show tabs and make blocks clickable when entering edit mode from the Styles menu. ([52222](https://github.com/WordPress/gutenberg/pull/52222)) + +#### Widgets Editor +- Add @example tags to the customize-widgets package. ([52141](https://github.com/WordPress/gutenberg/pull/52141)) + +#### NUX +- Page Content Focus: Add welcome guides. ([52014](https://github.com/WordPress/gutenberg/pull/52014)) + +#### Block Editor +- Use block label and icon for the inserter draggable chip.. ([51048](https://github.com/WordPress/gutenberg/pull/51048)) + +#### Design Tools +- Add Typography: Text orientation (writing mode). ([50822](https://github.com/WordPress/gutenberg/pull/50822)) + +#### Components +- RangeControl: Add support for large 40px number input size. ([49105](https://github.com/WordPress/gutenberg/pull/49105)) + + +### New APIs + +#### Block Editor +- Add new `registerInserterMediaCategory` API to make media categories extensible. ([51542](https://github.com/WordPress/gutenberg/pull/51542)) + + +### Bug Fixes + +- Adjust the position of sticky headings in preferences modal. ([52248](https://github.com/WordPress/gutenberg/pull/52248)) +- BlockRemovalWarningModal: Fix incorrect '_n' usage. ([52164](https://github.com/WordPress/gutenberg/pull/52164)) +- Editor initrial appender: Zero out margins in constrained layouts. ([52026](https://github.com/WordPress/gutenberg/pull/52026)) +- Export store from the edit-site package. ([51986](https://github.com/WordPress/gutenberg/pull/51986)) +- Fix disable DFM when opening styles command. ([52165](https://github.com/WordPress/gutenberg/pull/52165)) +- Fix unintentional toggling on of distraction free. ([52090](https://github.com/WordPress/gutenberg/pull/52090)) +- Footnotes: Increase selector specificity for anchor. ([52179](https://github.com/WordPress/gutenberg/pull/52179)) +- Respect custom aspect ratio. ([52286](https://github.com/WordPress/gutenberg/pull/52286)) +- Turn off DFM for style book and style editing. ([52117](https://github.com/WordPress/gutenberg/pull/52117)) +- Update fixed block toolbar. ([52123](https://github.com/WordPress/gutenberg/pull/52123)) +- Updating the BlockEditorProvider settings prop should reset the store's settings entirely. ([51904](https://github.com/WordPress/gutenberg/pull/51904)) +- [Command Palette]: Remove suggestion for deleting templates/parts. ([52168](https://github.com/WordPress/gutenberg/pull/52168)) +- [Command center]: Add preferences and keyboard shortcuts commands. ([51862](https://github.com/WordPress/gutenberg/pull/51862)) +- [Edit Post]: Add toggle fullscreen mode and list view commands. ([52184](https://github.com/WordPress/gutenberg/pull/52184)) + +#### Block Library +- Fix default block dimensions visibility. ([52256](https://github.com/WordPress/gutenberg/pull/52256)) +- Fix fetching Nav fallback ID flushing Navigation entity cache. ([52069](https://github.com/WordPress/gutenberg/pull/52069)) +- Fix flaky tests in `navigation.spec.js` and other tests related to the Post Editor Template mode. ([51790](https://github.com/WordPress/gutenberg/pull/51790)) +- Fix: Term Description block should only be available in the site editor. ([51053](https://github.com/WordPress/gutenberg/pull/51053)) +- Footnotes: Register meta field for pages. ([52024](https://github.com/WordPress/gutenberg/pull/52024)) +- Image block: Fix cursor style when lightbox is opened. ([52187](https://github.com/WordPress/gutenberg/pull/52187)) +- Navigation: Add the draft status to the navigation title. ([51967](https://github.com/WordPress/gutenberg/pull/51967)) +- Navigation: Fix end-to-end test failures caused by sidebar title change. ([52308](https://github.com/WordPress/gutenberg/pull/52308)) +- Navigation: Fix sidebar title. ([52167](https://github.com/WordPress/gutenberg/pull/52167)) +- Navigation: Remove one preloaded endpoint. ([52115](https://github.com/WordPress/gutenberg/pull/52115)) +- Page List: Fix parent block selection when converting to link. ([52193](https://github.com/WordPress/gutenberg/pull/52193)) +- Post editor: Require confirmation before removing Footnotes. ([52277](https://github.com/WordPress/gutenberg/pull/52277)) +- fix: Display heading level dropdown icons and labels. ([52004](https://github.com/WordPress/gutenberg/pull/52004)) + +#### Site Editor +- Add confirmation step when deleting a Template. ([52236](https://github.com/WordPress/gutenberg/pull/52236)) +- Command Palette: Fix incorrect path and snackbar message when template part is deleted. ([52034](https://github.com/WordPress/gutenberg/pull/52034)) +- Default to showing status slug in sidebar. ([52226](https://github.com/WordPress/gutenberg/pull/52226)) +- Fix missing MenuGroup segment in Site Editor header more menu. ([51860](https://github.com/WordPress/gutenberg/pull/51860)) +- Fix missing snackbars in Library. ([52021](https://github.com/WordPress/gutenberg/pull/52021)) +- Fix stepper styling in Home template sidebar. ([52025](https://github.com/WordPress/gutenberg/pull/52025)) +- Get the top toolbar preference from the correct scope. ([51840](https://github.com/WordPress/gutenberg/pull/51840)) +- Hide word count and reading time meta data for the Posts Page details panel. ([52186](https://github.com/WordPress/gutenberg/pull/52186)) +- Modal: Add small top padding to the content so that avoid cutting off the visible outline when hovering items. ([51829](https://github.com/WordPress/gutenberg/pull/51829)) +- Site Editor Frame: Ignore Spotlight in view mode. ([52262](https://github.com/WordPress/gutenberg/pull/52262)) +- Try restoring the site editor animation. ([51956](https://github.com/WordPress/gutenberg/pull/51956)) + +#### Patterns +- Fix custom patterns console error. ([51947](https://github.com/WordPress/gutenberg/pull/51947)) +- Fix history back after entering edit mode from Patterns. ([52112](https://github.com/WordPress/gutenberg/pull/52112)) +- Fix setting of sync status for fully synced patterns. ([51952](https://github.com/WordPress/gutenberg/pull/51952)) +- Fix sidebar tab label. ([51953](https://github.com/WordPress/gutenberg/pull/51953)) +- Fix: Pattern focus mode DocumentActions should use the pattern icon. ([52031](https://github.com/WordPress/gutenberg/pull/52031)) +- Include template parts for custom areas in Uncategorized category. ([52159](https://github.com/WordPress/gutenberg/pull/52159)) +- Remove ability for user to toggle sync status after pattern creation. ([51998](https://github.com/WordPress/gutenberg/pull/51998)) +- Rename sync_status and move to top level field on rest return instead of a meta field. ([52146](https://github.com/WordPress/gutenberg/pull/52146)) + +#### Interactivity API +- Block Image: Lightbox - Hide animation selector if behavior is Default or None. ([51748](https://github.com/WordPress/gutenberg/pull/51748)) +- Image block: Fix responsive sizing in lightbox. ([51823](https://github.com/WordPress/gutenberg/pull/51823)) +- Image block: Lightbox animation improvements. ([51721](https://github.com/WordPress/gutenberg/pull/51721)) +- Navigation block: Check that the modal is set before using `contains`. ([51962](https://github.com/WordPress/gutenberg/pull/51962)) + +#### Accessibility +- Fix incorrect aria-describedby attributes for theme patterns. ([52263](https://github.com/WordPress/gutenberg/pull/52263)) +- Guide: Place focus on the guide's container instead of its first tabbable. ([52300](https://github.com/WordPress/gutenberg/pull/52300)) +- Site Editor: Update headings hierarchy in the 'Manage all' screens. ([52271](https://github.com/WordPress/gutenberg/pull/52271)) + +#### Global Styles +- Check if experiment enabled for realsies this time. ([52315](https://github.com/WordPress/gutenberg/pull/52315)) +- Check randomizer experiment is enabled before rendering button. ([52306](https://github.com/WordPress/gutenberg/pull/52306)) + +#### Navigation Menu Sidebar +- Make the entire preview clickable in order to enter "edit" mode in focus mode. ([51973](https://github.com/WordPress/gutenberg/pull/51973)) +- Restore sidebar in focus mode on Pattern click through in Browse Mode `Library`. ([51897](https://github.com/WordPress/gutenberg/pull/51897)) + +#### Page Content Focus +- Hide parent selector when parent's block editing mode is 'disabled' or 'contentOnly'. ([52264](https://github.com/WordPress/gutenberg/pull/52264)) + +#### Post Editor +- Editor: Avoid remounting pre-publish sidebar contents during autosave. ([52208](https://github.com/WordPress/gutenberg/pull/52208)) + +#### Block Editor +- Enable draft entity creation in Nav block offcanvas. ([52166](https://github.com/WordPress/gutenberg/pull/52166)) + +#### History +- Update the behavior of the cached undo/redo stack. ([51644](https://github.com/WordPress/gutenberg/pull/51644)) + +#### Components +- DropdownMenu: Fix icon style when dashicon is used. ([43574](https://github.com/WordPress/gutenberg/pull/43574)) + + +### Performance + +- Migrate performance tests to Playwright. ([51084](https://github.com/WordPress/gutenberg/pull/51084)) +- Social links: Reverts updating class and style attributes. ([52019](https://github.com/WordPress/gutenberg/pull/52019)) +- tests: Configure as a production environment. ([52016](https://github.com/WordPress/gutenberg/pull/52016)) + +#### Block Library +- Try: Aggressive TinyMCE deprecation. ([50387](https://github.com/WordPress/gutenberg/pull/50387)) + + +### Experiments + +#### Interactivity API +- Create @wordpress/interactivity with the Interactivity API. ([50906](https://github.com/WordPress/gutenberg/pull/50906)) + + +### Documentation + +- Add @examples to the @wordpress/rich-text package selectors and hide the actions from documentation. ([52089](https://github.com/WordPress/gutenberg/pull/52089)) +- Add examples for core/keyboard-shortcut package. ([42831](https://github.com/WordPress/gutenberg/pull/42831)) +- Block Editor: Add README for FontFamilyControl component. ([52118](https://github.com/WordPress/gutenberg/pull/52118)) +- Block Editor: Add README for `PanelColorSettings` component. ([52327](https://github.com/WordPress/gutenberg/pull/52327)) +- Block Editor: Add README for `RecursionProvider`. ([52334](https://github.com/WordPress/gutenberg/pull/52334)) +- Docs: Update release documentation to use the right cherry-picking command. ([51935](https://github.com/WordPress/gutenberg/pull/51935)) + + +### Code Quality + +- Lodash: Refactor away from `_.kebabCase()` in `getCleanTemplatePartSlug`. ([51906](https://github.com/WordPress/gutenberg/pull/51906)) +- Lodash: Refactor away from `_.kebabCase()` in add page modal. ([51911](https://github.com/WordPress/gutenberg/pull/51911)) +- Lodash: Refactor away from `_.kebabCase()` in generic template modal. ([51910](https://github.com/WordPress/gutenberg/pull/51910)) +- Lodash: Remove completely from `@wordpress/style-engine` package. ([51726](https://github.com/WordPress/gutenberg/pull/51726)) + +#### Block Library +- Heading Block: Remove unused `HeadingLevelIcon` component. ([52008](https://github.com/WordPress/gutenberg/pull/52008)) +- Image block and behaviors: Fix some warnings. ([52109](https://github.com/WordPress/gutenberg/pull/52109)) +- Lodash: Refactor embed block away from `_.kebabCase()`. ([51916](https://github.com/WordPress/gutenberg/pull/51916)) +- Lodash: Remove dependency from block library package. ([51976](https://github.com/WordPress/gutenberg/pull/51976)) +- Make Navigation fallback selector private. ([51413](https://github.com/WordPress/gutenberg/pull/51413)) +- Page List: Fix ESLint warnings. ([52267](https://github.com/WordPress/gutenberg/pull/52267)) +- Refactor, document, and fix image block deprecations. ([52081](https://github.com/WordPress/gutenberg/pull/52081)) + +#### Page Content Focus +- Add basic test for the page content focus flow. ([52231](https://github.com/WordPress/gutenberg/pull/52231)) + +#### List View +- Return primitive value for 'hideInserter' in Appender component. ([52161](https://github.com/WordPress/gutenberg/pull/52161)) + +#### Interactivity API +- Fix the `exsisting` -> `existing` typo. ([52110](https://github.com/WordPress/gutenberg/pull/52110)) + +#### Navigation Menu Sidebar +- Remove redundant call to Navigation selector in Browse Mode. ([51988](https://github.com/WordPress/gutenberg/pull/51988)) + +#### Site Editor +- Block removal prompt: Let consumers pass their own rules. ([51841](https://github.com/WordPress/gutenberg/pull/51841)) + +#### Block Editor +- Revise LinkControl suggestions UI to use MenuItem. ([50978](https://github.com/WordPress/gutenberg/pull/50978)) + + +### Tools + +#### Testing +- Drops PHP 5.6 CI jobs. ([52345](https://github.com/WordPress/gutenberg/pull/52345)) +- Fix flakiness of saving entities in the site editor. ([51728](https://github.com/WordPress/gutenberg/pull/51728)) +- Fix flaky Site Editor pages end-to-end test. ([52283](https://github.com/WordPress/gutenberg/pull/52283)) +- Have `createNewPost` wait for editor canvas contents. ([51824](https://github.com/WordPress/gutenberg/pull/51824)) + +#### Build Tooling +- Fix phpunit failures. ([51950](https://github.com/WordPress/gutenberg/pull/51950)) +- Use moment-timezone-data-webpack-plugin to optimize timezones shipped in wp/date. ([51519](https://github.com/WordPress/gutenberg/pull/51519)) + + +### Various + +- Add caching to schema of REST API. ([52045](https://github.com/WordPress/gutenberg/pull/52045)) +- Add code owners for the Interactivity API runtime. ([52174](https://github.com/WordPress/gutenberg/pull/52174)) +- Backport from core: Rename `gutenberg_get_remote_theme_patterns` to `gutenberg_get_theme_directory_pattern_slugs`. ([51784](https://github.com/WordPress/gutenberg/pull/51784)) +- Block editor store: Also attach private APIs to old store descriptor. ([52088](https://github.com/WordPress/gutenberg/pull/52088)) +- Blocks: Remove gutenberg refs in PHP files. ([51978](https://github.com/WordPress/gutenberg/pull/51978)) +- Command palette: Rename. ([52153](https://github.com/WordPress/gutenberg/pull/52153)) +- Drop-indicator: Remove white border. ([52122](https://github.com/WordPress/gutenberg/pull/52122)) +- First version of the Interactivity API README. ([52104](https://github.com/WordPress/gutenberg/pull/52104)) +- Global Styles Revisions API: Backport changes from Core. ([52095](https://github.com/WordPress/gutenberg/pull/52095)) +- Global Styles Sidebar: Re-add Colors: Heading to selected blocks. ([49131](https://github.com/WordPress/gutenberg/pull/49131)) +- Image block: Update lightbox animation tests. ([52290](https://github.com/WordPress/gutenberg/pull/52290)) +- Patterns: Update section heading levels. ([52273](https://github.com/WordPress/gutenberg/pull/52273)) +- Perf logging: Change date to ISO 8601. ([51833](https://github.com/WordPress/gutenberg/pull/51833)) +- Refactor use-tab-nav shift+tab to use existing utils. ([51817](https://github.com/WordPress/gutenberg/pull/51817)) +- Remove serverSideBlockDefinitions from a test. ([52215](https://github.com/WordPress/gutenberg/pull/52215)) +- Restore "Buttons > can resize width" test. ([51865](https://github.com/WordPress/gutenberg/pull/51865)) +- Update delete page button label. ([51812](https://github.com/WordPress/gutenberg/pull/51812)) +- Update versions in WP for 6.3. ([51984](https://github.com/WordPress/gutenberg/pull/51984)) +- Wrap "Move to trash" and "Switch to draft" buttons when labels are too long to fit on a single row. ([52249](https://github.com/WordPress/gutenberg/pull/52249)) +- [Github-Actions-Workflows][Plugin-Release] Allow shipping a point-release for an older stable release. ([49082](https://github.com/WordPress/gutenberg/pull/49082)) + +#### Block Library +- Block Editor: Unify texts for Create pattern modal. ([52151](https://github.com/WordPress/gutenberg/pull/52151)) +- Block Supports: Change prefix in gutenberg_apply_colors_support to wp_ in dynamic blocks. ([51989](https://github.com/WordPress/gutenberg/pull/51989)) +- Navigation in Site View: Readd the edit button. ([52111](https://github.com/WordPress/gutenberg/pull/52111)) +- Navigation submenu: Remove unused doc block. ([52152](https://github.com/WordPress/gutenberg/pull/52152)) +- Page List: Change modal text. ([52116](https://github.com/WordPress/gutenberg/pull/52116)) +- i18n: Add context to the word "Filters". ([52198](https://github.com/WordPress/gutenberg/pull/52198)) + +#### Site Editor +- Library: Update icons in the creation menu. ([52108](https://github.com/WordPress/gutenberg/pull/52108)) +- Polish welcome guide copy for page / template editing. ([52282](https://github.com/WordPress/gutenberg/pull/52282)) +- Try: Update template titles. ([51428](https://github.com/WordPress/gutenberg/pull/51428)) +- Update stepper styling in Home template details panel. ([51972](https://github.com/WordPress/gutenberg/pull/51972)) +- Update text color of active menu items. ([51965](https://github.com/WordPress/gutenberg/pull/51965)) + +#### Patterns +- Add a hint about the rename of reusable blocks to menu and inserter. ([51771](https://github.com/WordPress/gutenberg/pull/51771)) +- Copy: "Detach pattern" instead of "Covert to regular block". ([51993](https://github.com/WordPress/gutenberg/pull/51993)) +- Library: Reinstate manage all template parts page. ([51961](https://github.com/WordPress/gutenberg/pull/51961)) +- [Library] Add lock icon for theme patterns. ([51990](https://github.com/WordPress/gutenberg/pull/51990)) + +#### Accessibility +- Navigation block: Do not toggle aria-expanded on hover when the overlay menu is opened. ([52170](https://github.com/WordPress/gutenberg/pull/52170)) +- Navigation block: Don't close submenu when it has focus. ([52177](https://github.com/WordPress/gutenberg/pull/52177)) + +#### Widgets Editor +- Export the store for the core/edit-widgets pacakage. ([52190](https://github.com/WordPress/gutenberg/pull/52190)) + +#### Post Editor +- Move block editor settings filter into 6.3 compat folder. ([52100](https://github.com/WordPress/gutenberg/pull/52100)) + +#### Layout +- Move grid function kses patch into 6.3 compat folder. ([52098](https://github.com/WordPress/gutenberg/pull/52098)) + +#### Data Layer +- hasResolvingSelectors: Exclude from result of resolveSelect. ([52038](https://github.com/WordPress/gutenberg/pull/52038)) + +#### Icons +- Remove fill="none" from pinSmall icon. ([51979](https://github.com/WordPress/gutenberg/pull/51979)) + +#### Navigation Menu Sidebar +- Sidebar Navigation: Refactor delete modal with `ConfirmDialog` component. ([51867](https://github.com/WordPress/gutenberg/pull/51867)) + +#### Templates API +- Template revisions API: Move back to experimental. ([51774](https://github.com/WordPress/gutenberg/pull/51774)) + + + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @ajlende @annezazu @artemiomorales @c4rl0sbr4v0 @carolinan @DAreRodz @dcalhoun @draganescu @ellatrix @fullofcaffeine @getdave @glendaviesnz @hellofromtonya @jameskoster @jasmussen @jeryj @jsnajdr @juanfra @juanmaguitar @kevin940726 @luisherranz @Mamaduka @mcsf @michalczaplinski @miminari @noisysocks @ntsekouras @oandregal @ockham @priethor @ramonjd @richtabor @ryanwelcher @SaxonF @scruffian @spacedmonkey @stokesman @t-hamano @talldan @tellthemachines @tyxla @WunderBart @youknowriad + + += 16.1.1 = + +## Changelog + +### Features + +#### Patterns + +- Remove ability for user to toggle sync status after pattern creation. This is because changing the sync status can have unintended consequences on existing instances of the block. ([52230](https://github.com/WordPress/gutenberg/pull/52230)) + +### Code Quality + +#### Patterns + +- Rename sync_status postmeta from `sync_status` to `wp_pattern_sync_status` to avoid clashes with custom plugin fields. ([52232](https://github.com/WordPress/gutenberg/pull/52232)) + +### Bugs + +#### Patterns + +- Fix issue with wrong sync status being set in the Site Editor when adding new synced patterns. ([52229 ](https://github.com/WordPress/gutenberg/pull/52229)) + +## Contributors + +The following contributors merged PRs in this release: + +@glendaviesnz + + = 16.1.0 = ## Changelog diff --git a/docs/contributors/code/react-native/getting-started-react-native.md b/docs/contributors/code/react-native/getting-started-react-native.md index 96338af7a6f2f7..9b8ae44f0d8b18 100644 --- a/docs/contributors/code/react-native/getting-started-react-native.md +++ b/docs/contributors/code/react-native/getting-started-react-native.md @@ -89,7 +89,7 @@ One of the extensions we are using is the [React Native Tools](https://marketpla Use the following command to run the test suite: ```sh -npm run native test +npm run test:native ``` It will run the [jest](https://github.com/facebook/jest) test runner on your tests. The tests are running on the desktop against Node.js. @@ -97,7 +97,7 @@ It will run the [jest](https://github.com/facebook/jest) test runner on your tes To run the tests with debugger support, start it with the following CLI command: ```sh -npm run native test:debug +npm run test:native:debug ``` Then, open `chrome://inspect` in Chrome to attach the debugger (look into the "Remote Target" section). While testing/developing, feel free to sprinkle `debugger` statements anywhere in the code that you'd like the debugger to break. diff --git a/docs/contributors/code/react-native/osx-setup-guide.md b/docs/contributors/code/react-native/osx-setup-guide.md index 5d6d4603e49598..dab5f33448b74d 100644 --- a/docs/contributors/code/react-native/osx-setup-guide.md +++ b/docs/contributors/code/react-native/osx-setup-guide.md @@ -212,7 +212,7 @@ After a bit of a wait, we’ll see something like this: ## Unit Tests ```sh -npm run native test +npm run test:native ``` ## Integration Tests diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index 180de30d195571..edbd67655a6990 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -181,7 +181,13 @@ _If_ however, the previous release was an **RC** (e.g. `X.Y.0-rc.1`) you will ne To do this, when running the Workflow, select the appropriate `release/` branch from the `Use workflow from` dropdown (e.g. `release/12.5`) and specify `stable` in the text input field. -Please note you **cannot create minor releases for previous stable releases once a more recent stable release has been published** as this would require significant changes to how we upload plugin versions to the WP.org plugin SVN repo). Always check the latest release version before you proceed (see [this Issue](https://github.com/WordPress/gutenberg/issues/33277#issuecomment-876289457) for more information). +##### Creating a minor release for previous stable releases + +It is possible to create a minor release for any release branch even after a more recent stable release has been published. This can be done for _any_ previous release branches, allowing more flexibility in delivering updates to users. In the past, users had to wait for the next stable release, potentially taking days. Now, fixes can be swiftly shipped to any previous release branches as required. + +The process is identical to the one documented above when an RC is already out: choose a previous release branch, type `stable`, and click "Run workflow". The release will be published on the GitHub releases page for Gutenberg and to the WordPress core repository SVN as a `tag` under http://plugins.svn.wordpress.org/gutenberg/tags/. The SVN `trunk` directory will not be touched. + +**IMPORTANT:** When publishing the draft created by the ["Build Plugin Zip" workflow](https://github.com/WordPress/gutenberg/actions/workflows/build-plugin-zip.yml), make sure to leave the "Set as last release" checkbox unchecked. If it is left checked by accident, the ["Upload Gutenberg plugin to WordPress.org plugin" workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) will still correctly upload it **as a tag (and will _not_ replace the `trunk` version)** to the WordPress plugin repository SVN - the workflow will perform some version arithmetic to determine how the plugin should be shipped - but you'll still need to fix the state on GitHub by setting the right release as `latest` on the [releases](https://github.com/WordPress/gutenberg/releases/) page! #### Troubleshooting diff --git a/docs/contributors/code/testing-overview.md b/docs/contributors/code/testing-overview.md index 42704be233b9b9..946aec8cd70e3e 100644 --- a/docs/contributors/code/testing-overview.md +++ b/docs/contributors/code/testing-overview.md @@ -497,7 +497,7 @@ Part of the unit-tests suite is a set of Jest tests run exercise native-mobile c To locally run the tests in debug mode, follow these steps: 0. Make sure you have ran `npm ci` to install all the packages -1. Run `npm run native test:debug` inside the Gutenberg root folder, on the CLI. Node is now waiting for the debugger to connect. +1. Run `npm run test:native:debug` inside the Gutenberg root folder, on the CLI. Node is now waiting for the debugger to connect. 2. Open `chrome://inspect` in Chrome 3. Under the "Remote Target" section, look for a `../../node_modules/.bin/jest` target and click on the "inspect" link. That will open a new window with the Chrome DevTools debugger attached to the process and stopped at the beginning of the `jest.js` file. Alternatively, if the targets are not visible, click on the `Open dedicated DevTools for Node` link in the same page. 4. You can place breakpoints or `debugger;` statements throughout the code, including the tests code, to stop and inspect diff --git a/docs/explanations/architecture/entities.md b/docs/explanations/architecture/entities.md index 13e6eaca08b5a0..2a3af6288d27fa 100644 --- a/docs/explanations/architecture/entities.md +++ b/docs/explanations/architecture/entities.md @@ -56,8 +56,14 @@ For example, let's say a user edits the title of a post, followed by a modificat The store also keep tracks of a "pointer" to the current "undo/redo" step. By default, the pointer always points to the last item in the stack. This pointer is updated when the user performs an undo or redo operation. -### Transient changes +### Cached changes -The undo/redo core behavior also supports what we call "transient modifications". These are modifications that are not stored in the undo/redo stack right away. For instance, when a user starts typing in a text field, the value of the field is modified in the store, but this modification is not stored in the undo/redo stack until after the user moves to the next word or after a few milliseconds. This is done to avoid creating a new undo/redo step for each character typed by the user. +The undo/redo core behavior also supports what we call "cached modifications". These are modifications that are not stored in the undo/redo stack right away. For instance, when a user starts typing in a text field, the value of the field is modified in the store, but this modification is not stored in the undo/redo stack until after the user moves to the next word or after a few milliseconds. This is done to avoid creating a new undo/redo step for each character typed by the user. -So by default, `core-data` store considers all modifications to properties that are marked as "transient" (like the `blocks` property in the post entity) as transient modifications. It keeps these modifications outside the undo/redo stack in what is called a "cache" of modifications and these modifications are only stored in the undo/redo stack when we explicitely call `__unstableCreateUndoLevel` or when the next non-transient modification is performed. +Cached changes are kept outside the undo/redo stack in what is called a "cache" of modifications and these modifications are only stored in the undo/redo stack when we explicitely call `__unstableCreateUndoLevel` or when the next modification is not a cached one. + +By default all calls to `editEntityRecord` are considered "non-cached" unless the `isCached` option is passed as true. Example: + +```js +wp.data.dispatch( 'core' ).editEntityRecord( 'postType', 'post', 1, { title: 'Hello World' }, { isCached: true } ); +``` diff --git a/docs/explanations/architecture/styles.md b/docs/explanations/architecture/styles.md index faf2649efd13ab..a8a5af72fec76b 100644 --- a/docs/explanations/architecture/styles.md +++ b/docs/explanations/architecture/styles.md @@ -59,10 +59,8 @@ The user may change the state of this block by applying different styles: a text After some user modifications to the block, the initial markup may become something like this: ```html -

+

``` This is what we refer to as "user-provided block styles", also know as "local styles" or "serialized styles". Essentially, each tool (font size, color, etc) ends up adding some classes and/or inline styles to the block markup. The CSS styling for these classes is part of the block, global, or theme stylesheets. @@ -511,9 +509,7 @@ The global styles UI in the site editor has a screen for per-block styles. The l In addition to styles at the individual block level and in global styles, there is the concept of layout styles that are output for both blocks-based and classic themes. -The layout block support is an experimental approach for outputting common layout styles that are shared between blocks that are used for creating layouts. Layout styles are useful for providing common styling for any block that is a container for other blocks. Examples of blocks that depend on these layout styles include Group, Row, Columns, Buttons, and Social Icons. - -While the feature is part of WordPress core, it is still flagged as experimental in the sense that the features and output are still undergoing active development. It is therefore not yet a stable feature from the perspective of 3rd party blocks, as the API is likely to change. The feature is enabled in core blocks via the `layout` setting under `supports` in a block's `block.json` file. +The layout block support outputs common layout styles that are shared between blocks used for creating layouts. Layout styles are useful for providing common styling for any block that is a container for other blocks. Examples of blocks that depend on these layout styles include Group, Row, Columns, Buttons, and Social Icons. The feature is enabled in core blocks via the `layout` setting under `supports` in a block's `block.json` file. There are two primary places where Layout styles are output: @@ -523,22 +519,23 @@ Base layout styles are those styles that are common to all blocks that opt in to Base layout styles are output from within [the main PHP class](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/class-wp-theme-json.php) that handles global styles, and form part of the global styles stylesheet. In order to provide support for core blocks in classic themes, these styles are always output, irrespective of whether the theme provides its own `theme.json` file. -Common layout definitions are stored in [the core `theme.json` file](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/theme.json), but are not intended to be extended or overridden by themes. +Common layout definitions are stored in [the core layout block support file](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php). #### Individual layout styles -When a block that opts in to the experimental layout support is rendered, two things are processed and added to the output via [`layout.php`](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php): +When a block that opts in to layout support is rendered, two things are processed and added to the output via [`layout.php`](https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/layout.php): - Semantic class names are added to the block markup to indicate which layout settings are in use. For example, `is-layout-flow` is for blocks (such as Group) that use the default/flow layout, and `is-content-justification-right` is added when a user sets a block to use right justification. - Individual styles are generated for non-default layout values that are set on the individual block being rendered. These styles are attached to the block via a container class name using the form `wp-container-$id` where the `$id` is a [unique number](https://developer.wordpress.org/reference/functions/wp_unique_id/). #### Available layout types -There are currently three layout types in use: +There are currently four layout types in use: - Default/Flow: Items are stacked vertically. The parent container block is set to `display: flow` and the spacing between children is handled via vertical margins. - Constrained: Items are stacked vertically, using the same spacing logic as the Flow layout. Features constrained widths for child content, outputting widths for standard content size and wide size. Defaults to using global `contentSize` and `wideSize` values set in `settings.layout` in the `theme.json`. - Flex: Items are displayed using a Flexbox layout. Defaults to a horizontal orientation. Spacing between children is handled via the `gap` CSS property. +- Grid: Items are displayed using a Grid layout. Defaults to an `auto-fill` approach to column generation but can also be set to a fixed number of columns. Spacing between children is handled via the `gap` CSS property. For controlling spacing between blocks, and enabling block spacing controls see: [What is blockGap and how can I use it?](https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/#what-is-blockgap-and-how-can-i-use-it). @@ -546,7 +543,7 @@ For controlling spacing between blocks, and enabling block spacing controls see: The layout block support is designed to enable control over layout features from within the block and site editors. Where possible, try to use the features of the blocks to determine particular layout requirements rather than relying upon additional stylesheets. -For themes that wish to target container blocks in order to add or adjust particular styles, the block's class name is often the best class name to use. Class names such as `wp-block-group` or `wp-block-columns` are usually reliable class names for targeting a particular block. +For themes that wish to target container blocks in order to add or adjust particular styles, the block's class name is often the best class name to use. Class names such as `wp-block-group` or `wp-block-columns` are usually reliable class names for targeting a particular block. In addition to block and layout classnames, there is also a classname composed of block and layout together: for example, for a Group block with a constrained layout it will be `wp-block-group-is-layout-constrained`. For targeting a block that uses a particular layout type, avoid targeting `wp-container-` as container classes may not always be present in the rendered markup. @@ -559,6 +556,7 @@ The current semantic class names that can be output by the Layout block support - `is-layout-flow`: Blocks that use the Default/Flow layout type. - `is-layout-constrained`: Blocks that use the Constrained layout type. - `is-layout-flex`: Blocks that use the Flex layout type. +- `is-layout-grid`: Blocks that used the Grid layout type. - `wp-container-$id`: Where `$id` is a semi-random number. A container class that only exists when the block contains non-default Layout values. This class should not be used directly for any CSS targeting as it may or may not be present. - `is-horizontal`: When a block explicitly sets `orientation` to `horizontal`. - `is-vertical`: When a block explicitly sets `orientation` to `vertical`. diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 6f73ea9814c9d2..951accef517787 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -275,7 +275,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb - **Name:** core/footnotes - **Category:** text -- **Supports:** ~~html~~, ~~inserter~~, ~~multiple~~, ~~reusable~~ +- **Supports:** ~~html~~, ~~multiple~~, ~~reusable~~ - **Attributes:** ## Classic diff --git a/docs/reference-guides/data/data-core-customize-widgets.md b/docs/reference-guides/data/data-core-customize-widgets.md index 13476f94fdb398..78433e8991a81c 100644 --- a/docs/reference-guides/data/data-core-customize-widgets.md +++ b/docs/reference-guides/data/data-core-customize-widgets.md @@ -10,6 +10,25 @@ Namespace: `core/customize-widgets`. Returns true if the inserter is opened. +_Usage_ + +```js +import { store as customizeWidgetsStore } from '@wordpress/customize-widgets'; +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const { isInserterOpened } = useSelect( + ( select ) => select( customizeWidgetsStore ), + [] + ); + + return isInserterOpened() + ? __( 'Inserter is open' ) + : __( 'Inserter is closed.' ); +}; +``` + _Parameters_ - _state_ `Object`: Global application state. @@ -28,6 +47,32 @@ _Returns_ Returns an action object used to open/close the inserter. +_Usage_ + +```js +import { store as customizeWidgetsStore } from '@wordpress/customize-widgets'; +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; +import { Button } from '@wordpress/components'; +import { useState } from '@wordpress/element'; + +const ExampleComponent = () => { + const { setIsInserterOpened } = useDispatch( customizeWidgetsStore ); + const [ isOpen, setIsOpen ] = useState( false ); + + return ( + + ); +}; +``` + _Parameters_ - _value_ `boolean|Object`: Whether the inserter should be opened (true) or closed (false). To specify an insertion point, use an object. diff --git a/gutenberg.php b/gutenberg.php index f0c0bf22545b1a..65b712ac1f76a3 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.1 * Requires PHP: 5.6 - * Version: 16.1.0 + * Version: 16.2.0-rc.3 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/blocks.php b/lib/blocks.php index 8185567db1b804..e98f711b5c85a5 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -372,3 +372,31 @@ function gutenberg_register_legacy_social_link_blocks() { } add_action( 'init', 'gutenberg_register_legacy_social_link_blocks' ); + +/** + * Migrate the legacy `sync_status` meta key (added 16.1) to the new `wp_pattern_sync_status` meta key (16.1.1). + * + * This filter is INTENTIONALLY left out of core as the meta key was fist introduced to core in 6.3 as `wp_pattern_sync_status`. + * see https://github.com/WordPress/gutenberg/pull/52232 + * + * @param mixed $value The value to return, either a single metadata value or an array of values depending on the value of $single. + * @param int $object_id ID of the object metadata is for. + * @param string $meta_key Metadata key. + * @param bool $single Whether to return only the first value of the specified $meta_key. + */ +function gutenberg_legacy_wp_block_post_meta( $value, $object_id, $meta_key, $single ) { + if ( 'wp_pattern_sync_status' !== $meta_key ) { + return $value; + } + + $sync_status = get_post_meta( $object_id, 'sync_status', $single ); + + if ( $single && 'unsynced' === $sync_status ) { + return $sync_status; + } elseif ( isset( $sync_status[0] ) && 'unsynced' === $sync_status[0] ) { + return $sync_status; + } + + return $value; +} +add_filter( 'default_post_metadata', 'gutenberg_legacy_wp_block_post_meta', 10, 4 ); diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 6e9d05cd7f238b..1e825e3c6bbe4f 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -275,7 +275,9 @@ public static function get_theme_data( $deprecated = array(), $options = array() } // BEGIN OF EXPERIMENTAL CODE. Not to backport to core. - static::$theme = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( static::$theme ); + if ( ! class_exists( 'WP_Font_Face' ) && class_exists( 'WP_Fonts_Resolver' ) ) { + static::$theme = WP_Fonts_Resolver::add_missing_fonts_to_theme_json( static::$theme ); + } // END OF EXPERIMENTAL CODE. } diff --git a/lib/client-assets.php b/lib/client-assets.php index 9757e4b7ff24a8..99aa7f147ecbfc 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -203,7 +203,13 @@ function gutenberg_register_packages_scripts( $scripts ) { // Add dependencies that cannot be detected and generated by build tools. switch ( $handle ) { case 'wp-block-library': - array_push( $dependencies, 'editor' ); + if ( + ! gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) || + ! empty( $_GET['requiresTinymce'] ) || + gutenberg_post_being_edited_requires_classic_block() + ) { + array_push( $dependencies, 'editor' ); + } break; case 'wp-edit-post': diff --git a/lib/compat/wordpress-6.3/blocks.php b/lib/compat/wordpress-6.3/blocks.php index b338d0a2467096..ccc68786dc6adb 100644 --- a/lib/compat/wordpress-6.3/blocks.php +++ b/lib/compat/wordpress-6.3/blocks.php @@ -60,6 +60,7 @@ function gutenberg_rename_reusable_block_cpt_to_pattern( $args, $post_type ) { $args['labels']['item_reverted_to_draft'] = __( 'Pattern reverted to draft.' ); $args['labels']['item_scheduled'] = __( 'Pattern scheduled.' ); $args['labels']['item_updated'] = __( 'Pattern updated.' ); + $args['rest_controller_class'] = 'Gutenberg_REST_Blocks_Controller'; } return $args; @@ -89,7 +90,7 @@ function gutenberg_add_custom_fields_to_wp_block( $args, $post_type ) { add_filter( 'register_post_type_args', 'gutenberg_add_custom_fields_to_wp_block', 10, 2 ); /** - * Adds sync_status meta fields to the wp_block post type so an unsynced option can be added. + * Adds wp_pattern_sync_status meta fields to the wp_block post type so an unsynced option can be added. * * Note: This should be removed when the minimum required WP version is >= 6.3. * @@ -101,39 +102,21 @@ function gutenberg_wp_block_register_post_meta() { $post_type = 'wp_block'; register_post_meta( $post_type, - 'sync_status', + 'wp_pattern_sync_status', array( 'auth_callback' => function() { return current_user_can( 'edit_posts' ); }, - 'sanitize_callback' => 'gutenberg_wp_block_sanitize_post_meta', + 'sanitize_callback' => 'sanitize_text_field', 'single' => true, 'type' => 'string', 'show_in_rest' => array( 'schema' => array( - 'type' => 'string', - 'properties' => array( - 'sync_status' => array( - 'type' => 'string', - ), - ), + 'type' => 'string', + 'enum' => array( 'partial', 'unsynced' ), ), ), ) ); } -/** - * Sanitizes the array of wp_block post meta sync_status string. - * - * Note: This should be removed when the minimum required WP version is >= 6.3. - * - * @see https://github.com/WordPress/gutenberg/pull/51144 - * - * @param array $meta_value String to sanitize. - * - * @return array Sanitized string. - */ -function gutenberg_wp_block_sanitize_post_meta( $meta_value ) { - return sanitize_text_field( $meta_value ); -} add_action( 'init', 'gutenberg_wp_block_register_post_meta' ); diff --git a/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php b/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php index 91417971e22c73..fcf6e13b0954d7 100644 --- a/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php +++ b/lib/compat/wordpress-6.3/class-gutenberg-navigation-fallback.php @@ -23,9 +23,18 @@ class Gutenberg_Navigation_Fallback { */ public static function get_fallback() { + /** + * Filters whether or not a fallback should be created. + * + * @since 6.3.0 + * + * @param bool Whether or not to create a fallback. + */ + $should_create_fallback = apply_filters( 'gutenberg_navigation_should_create_fallback', true ); + $fallback = static::get_most_recently_published_navigation(); - if ( $fallback ) { + if ( $fallback || ! $should_create_fallback ) { return $fallback; } diff --git a/lib/compat/wordpress-6.3/class-gutenberg-rest-blocks-controller.php b/lib/compat/wordpress-6.3/class-gutenberg-rest-blocks-controller.php new file mode 100644 index 00000000000000..5279a2c3a829ec --- /dev/null +++ b/lib/compat/wordpress-6.3/class-gutenberg-rest-blocks-controller.php @@ -0,0 +1,51 @@ + 'edit', - 'per_page' => 100, - 'order' => 'desc', - 'orderby' => 'date', - '_locale' => 'user', - // array indices are required to avoid query being encoded and not matching in cache. - 'status[0]' => 'publish', - 'status[1]' => 'draft', - ), - $navigation_rest_route - ), - 'GET', - ); - // Preload request for all menus in Browse Mode sidebar "Navigation" section. $preload_paths[] = array( add_query_arg( diff --git a/lib/compat/wordpress-6.3/script-loader.php b/lib/compat/wordpress-6.3/script-loader.php index c515eb10fdc6bb..8f7bda2a648114 100644 --- a/lib/compat/wordpress-6.3/script-loader.php +++ b/lib/compat/wordpress-6.3/script-loader.php @@ -81,7 +81,6 @@ function _gutenberg_get_iframed_editor_assets() { ob_start(); wp_print_styles(); - wp_print_fonts( true ); $styles = ob_get_clean(); ob_start(); diff --git a/lib/compat/wordpress-6.3/theme-previews.php b/lib/compat/wordpress-6.3/theme-previews.php index eab05c5824b1ff..26153d74878b58 100644 --- a/lib/compat/wordpress-6.3/theme-previews.php +++ b/lib/compat/wordpress-6.3/theme-previews.php @@ -88,7 +88,7 @@ function addLivePreviewButton() { livePreviewButton.setAttribute('class', 'button button-primary'); livePreviewButton.setAttribute( 'href', - `/wp-admin/site-editor.php?wp_theme_preview=${themePath}&return=themes.php` + `?wp_theme_preview=${themePath}&return=themes.php` ); livePreviewButton.innerHTML = ''; themeInfo.querySelector('.theme-actions').appendChild(livePreviewButton); @@ -107,7 +107,7 @@ function block_theme_activate_nonce() { $nonce_handle = 'switch-theme_' . gutenberg_get_theme_preview_path(); ?> window.wp.needsClassicBlock = true;'; +} +add_action( 'admin_footer', 'gutenberg_declare_classic_block_necessary' ); + +// If user has already requested TinyMCE, we're ending the experiment. +if ( ! empty( $_GET['requiresTinymce'] ) || gutenberg_post_being_edited_requires_classic_block() ) { + return; +} + + +/** + * Disable TinyMCE by introducing a placeholder `_WP_Editors` class. + */ +function gutenberg_disable_tinymce() { + require __DIR__ . '/class--wp-editors.php'; +} + +add_action( 'admin_init', 'gutenberg_disable_tinymce' ); + +/** + * Enqueue TinyMCE proxy script. + * Detects TinyMCE usage and sets the `requiresTinymce` query argument to stop disabling TinyMCE loading. + */ +function gutenberg_enqueue_tinymce_proxy() { + wp_enqueue_script( 'gutenberg-tinymce-proxy', plugins_url( 'assets/tinymce-proxy.js', __FILE__ ) ); +} + +add_action( 'admin_enqueue_scripts', 'gutenberg_enqueue_tinymce_proxy' ); + +/** + * Example TinyMCE usage used for testing. + * Uncomment line 8 in this file to enable. + */ +function gutenberg_test_tinymce_access() { + echo ''; +} + +/** + * Whether the current editor contains a classic block instance. + * + * @return bool True if the editor contains a classic block, false otherwse. + */ +function gutenberg_post_being_edited_requires_classic_block() { + if ( ! is_admin() ) { + return false; + } + + // Handle the post editor. + if ( ! empty( $_GET['post'] ) && ! empty( $_GET['action'] ) && 'edit' === $_GET['action'] ) { + $current_post = get_post( intval( $_GET['post'] ) ); + if ( ! $current_post || is_wp_error( $current_post ) ) { + return false; + } + + $content = $current_post->post_content; + } + + // Check if block editor is disabled by "Classic Editor" or another plugin. + if ( + function_exists( 'use_block_editor_for_post_type' ) && + ! use_block_editor_for_post_type( $current_post->post_type ) + ) { + return true; + } + + if ( empty( $content ) ) { + return false; + } + + $parsed_blocks = parse_blocks( $content ); + foreach ( $parsed_blocks as $block ) { + if ( empty( $block['blockName'] ) && strlen( trim( $block['innerHTML'] ) ) > 0 ) { + return true; + } + } + + return false; +} diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index eaa314946ab9a3..15d964e2deec72 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -90,6 +90,9 @@ function gutenberg_enable_experiments() { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalInteractivityAPI = true', 'before' ); } + if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { + wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' ); + } } add_action( 'admin_init', 'gutenberg_enable_experiments' ); diff --git a/lib/experimental/fonts-api/fonts-api.php b/lib/experimental/fonts-api/fonts-api.php index 841efeda47a953..8d07dc118f56e1 100644 --- a/lib/experimental/fonts-api/fonts-api.php +++ b/lib/experimental/fonts-api/fonts-api.php @@ -26,14 +26,7 @@ function wp_fonts() { // Initialize. $wp_fonts->register_provider( 'local', 'WP_Fonts_Provider_Local' ); add_action( 'wp_head', 'wp_print_fonts', 50 ); - - /* - * For themes without a theme.json, admin printing is initiated by the 'admin_print_styles' hook. - * For themes with theme.json, admin printing is initiated by _wp_get_iframed_editor_assets(). - */ - if ( ! wp_theme_has_theme_json() ) { - add_action( 'admin_print_styles', 'wp_print_fonts', 50 ); - } + add_action( 'admin_print_styles', 'wp_print_fonts', 50 ); } return $wp_fonts; @@ -250,3 +243,17 @@ static function( $mime_types ) { * during the build. See: tools/webpack/blocks.js. */ add_action( 'init', 'WP_Fonts_Resolver::register_fonts_from_theme_json', 21 ); + +add_filter( + 'block_editor_settings_all', + static function( $settings ) { + ob_start(); + wp_print_fonts( true ); + $styles = ob_get_clean(); + + // Add the font-face styles to iframed editor assets. + $settings['__unstableResolvedAssets']['styles'] .= $styles; + return $settings; + }, + 11 +); diff --git a/lib/experimental/fonts/class-wp-font-face-resolver.php b/lib/experimental/fonts/class-wp-font-face-resolver.php new file mode 100644 index 00000000000000..16e74d6051aa74 --- /dev/null +++ b/lib/experimental/fonts/class-wp-font-face-resolver.php @@ -0,0 +1,158 @@ +get_settings(); + + // Bail out early if there are no font settings. + if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) { + return array(); + } + + return static::parse_settings( $settings ); + } + + /** + * Parse theme.json settings to extract font definitions with variations grouped by font-family. + * + * @since X.X.X + * + * @param array $settings Font settings to parse. + * @return array Returns an array of fonts, grouped by font-family. + */ + private static function parse_settings( array $settings ) { + $fonts = array(); + + foreach ( $settings['typography']['fontFamilies'] as $font_families ) { + foreach ( $font_families as $definition ) { + + // Skip if font-family "name" is not defined. + if ( empty( $definition['name'] ) ) { + continue; + } + + // Skip if "fontFace" is not defined, meaning there are no variations. + if ( empty( $definition['fontFace'] ) ) { + continue; + } + + $font_family = $definition['name']; + + // Prepare the fonts array structure for this font-family. + if ( ! array_key_exists( $font_family, $fonts ) ) { + $fonts[ $font_family ] = array(); + } + + $fonts[ $font_family ] = static::convert_font_face_properties( $definition['fontFace'], $font_family ); + } + } + + return $fonts; + } + + /** + * Converts font-face properties from theme.json format. + * + * @since X.X.X + * + * @param array $font_face_definition The font-face definitions to convert. + * @param string $font_family_property The value to store in the font-face font-family property. + * @return array Converted font-face properties. + */ + private static function convert_font_face_properties( array $font_face_definition, $font_family_property ) { + $converted_font_faces = array(); + + foreach ( $font_face_definition as $font_face ) { + // Add the font-family property to the font-face. + $font_face['font-family'] = $font_family_property; + + // Converts the "file:./" src placeholder into a theme font file URI. + if ( ! empty( $font_face['src'] ) ) { + $font_face['src'] = static::to_theme_file_uri( (array) $font_face['src'] ); + } + + // Convert camelCase properties into kebab-case. + $font_face = static::to_kebab_case( $font_face ); + + $converted_font_faces[] = $font_face; + } + + return $converted_font_faces; + } + + /** + * Converts each 'file:./' placeholder into a URI to the font file in the theme. + * + * The 'file:./' is specified in the theme's `theme.json` as a placeholder to be + * replaced with the URI to the font file's location in the theme. When a "src" + * beings with this placeholder, it is replaced, converting the src into a URI. + * + * @since X.X.X + * + * @param array $src An array of font file sources to process. + * @return array An array of font file src URI(s). + */ + private static function to_theme_file_uri( array $src ) { + $placeholder = 'file:./'; + + foreach ( $src as $src_key => $src_url ) { + // Skip if the src doesn't start with the placeholder, as there's nothing to replace. + if ( ! str_starts_with( $src_url, $placeholder ) ) { + continue; + } + + $src_file = str_replace( $placeholder, '', $src_url ); + $src[ $src_key ] = get_theme_file_uri( $src_file ); + } + + return $src; + } + + /** + * Converts all first dimension keys into kebab-case. + * + * @since X.X.X + * + * @param array $data The array to process. + * @return array Data with first dimension keys converted into kebab-case. + */ + private static function to_kebab_case( array $data ) { + foreach ( $data as $key => $value ) { + $kebab_case = _wp_to_kebab_case( $key ); + $data[ $kebab_case ] = $value; + if ( $kebab_case !== $key ) { + unset( $data[ $key ] ); + } + } + + return $data; + } +} diff --git a/lib/experimental/fonts/class-wp-font-face.php b/lib/experimental/fonts/class-wp-font-face.php new file mode 100644 index 00000000000000..482bf4d42396d3 --- /dev/null +++ b/lib/experimental/fonts/class-wp-font-face.php @@ -0,0 +1,418 @@ + '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ); + + /** + * Valid font-face property names. + * + * @since X.X.X + * + * @var string[] + */ + private $valid_font_face_properties = array( + 'ascent-override', + 'descent-override', + 'font-display', + 'font-family', + 'font-stretch', + 'font-style', + 'font-weight', + 'font-variant', + 'font-feature-settings', + 'font-variation-settings', + 'line-gap-override', + 'size-adjust', + 'src', + 'unicode-range', + ); + + /** + * Valid font-display values. + * + * @since X.X.X + * + * @var string[] + */ + private $valid_font_display = array( 'auto', 'block', 'fallback', 'swap', 'optional' ); + + /** + * Array of font-face style tag's attribute(s) + * where the key is the attribute name and the + * value is its value. + * + * @since X.X.X + * + * @var string[] + */ + private $style_tag_attrs = array(); + + /** + * Creates and initializes an instance of WP_Font_Face. + * + * @since X.X.X + */ + public function __construct() { + /** + * Filters the font-face property defaults. + * + * @since X.X.X + * + * @param array $defaults { + * An array of required font-face properties and defaults. + * + * @type string $provider The provider ID. Default 'local'. + * @type string $font-family The font-family property. Default empty string. + * @type string $font-style The font-style property. Default 'normal'. + * @type string $font-weight The font-weight property. Default '400'. + * @type string $font-display The font-display property. Default 'fallback'. + * } + */ + $this->font_face_property_defaults = apply_filters( 'wp_font_face_property_defaults', $this->font_face_property_defaults ); + + if ( + function_exists( 'is_admin' ) && ! is_admin() + && + function_exists( 'current_theme_supports' ) && ! current_theme_supports( 'html5', 'style' ) + ) { + $this->style_tag_attrs = array( 'type' => 'text/css' ); + } + } + + /** + * Generates and prints the `@font-face` styles for the given fonts. + * + * @since X.X.X + * + * @param array $fonts The fonts to generate and print @font-face styles. + */ + public function generate_and_print( array $fonts ) { + $fonts = $this->validate_fonts( $fonts ); + + // Bail out if there are no fonts are given to process. + if ( empty( $fonts ) ) { + return; + } + + printf( + $this->get_style_element(), + $this->get_css( $fonts ) + ); + } + + /** + * Validates each of the font-face properties. + * + * @since X.X.X + * + * @param array $fonts The fonts to valid. + * @return array Prepared font-faces organized by provider and font-family. + */ + private function validate_fonts( array $fonts ) { + $validated_fonts = array(); + + foreach ( $fonts as $font_faces ) { + foreach ( $font_faces as $font_face ) { + $font_face = $this->validate_font_face_properties( $font_face ); + // Skip if failed validation. + if ( false === $font_face ) { + continue; + } + + $validated_fonts[] = $font_face; + } + } + + return $validated_fonts; + } + + /** + * Validates each font-face property. + * + * @since X.X.X + * + * @param array $font_face Font face properties to validate. + * @return false|array Validated font-face on success. Else, false. + */ + private function validate_font_face_properties( array $font_face ) { + $font_face = wp_parse_args( $font_face, $this->font_face_property_defaults ); + + // Check the font-family. + if ( empty( $font_face['font-family'] ) || ! is_string( $font_face['font-family'] ) ) { + trigger_error( 'Font font-family must be a non-empty string.' ); + return false; + } + + // Make sure that local fonts have 'src' defined. + if ( empty( $font_face['src'] ) || ( ! is_string( $font_face['src'] ) && ! is_array( $font_face['src'] ) ) ) { + trigger_error( 'Font src must be a non-empty string or an array of strings.' ); + return false; + } + + // Validate the 'src' property. + if ( ! empty( $font_face['src'] ) ) { + foreach ( (array) $font_face['src'] as $src ) { + if ( empty( $src ) || ! is_string( $src ) ) { + trigger_error( 'Each font src must be a non-empty string.' ); + return false; + } + } + } + + // Check the font-weight. + if ( ! is_string( $font_face['font-weight'] ) && ! is_int( $font_face['font-weight'] ) ) { + trigger_error( 'Font font-weight must be a properly formatted string or integer.' ); + return false; + } + + // Check the font-display. + if ( ! in_array( $font_face['font-display'], $this->valid_font_display, true ) ) { + $font_face['font-display'] = $this->font_face_property_defaults['font-display']; + } + + // Remove invalid properties. + foreach ( $font_face as $prop => $value ) { + if ( ! in_array( $prop, $this->valid_font_face_properties, true ) ) { + unset( $font_face[ $prop ] ); + } + } + + return $font_face; + } + + /** + * Gets the `\n"; + } + + /** + * Gets the defined ' + - ( assets?.styles ?? '' ); + const html = ` + + + + + ${ styles } + ${ scripts } + + + + +`; const [ src, cleanup ] = useMemo( () => { const _src = URL.createObjectURL( diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 5876eb4ec01e9e..db93f112a366d3 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -165,3 +165,8 @@ export { default as __experimentalInspectorPopoverHeader } from './inspector-pop export { default as BlockEditorProvider } from './provider'; export { default as useSetting } from './use-setting'; + +/* + * The following rename hint component can be removed in 6.4. + */ +export { default as ReusableBlocksRenameHint } from './inserter/reusable-block-rename-hint'; diff --git a/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js new file mode 100644 index 00000000000000..09861d9b97f1c9 --- /dev/null +++ b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js @@ -0,0 +1,52 @@ +/** + * WordPress dependencies + */ +import { Button } from '@wordpress/components'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { focus } from '@wordpress/dom'; +import { useRef } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { close } from '@wordpress/icons'; +import { store as preferencesStore } from '@wordpress/preferences'; + +const PREFERENCE_NAME = 'isResuableBlocksrRenameHintVisible'; + +export default function ReusableBlocksRenameHint() { + const isReusableBlocksRenameHint = useSelect( + ( select ) => + select( preferencesStore ).get( 'core', PREFERENCE_NAME ) ?? true, + [] + ); + + const ref = useRef(); + + const { set: setPreference } = useDispatch( preferencesStore ); + if ( ! isReusableBlocksRenameHint ) { + return null; + } + + return ( +
+
+ { __( + 'Reusable blocks are now called patterns. A synced pattern will behave in exactly the same way as a reusable block.' + ) } +
+
+ ); +} diff --git a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js index c16d5f1a78e543..08cd8d57ba0d0e 100644 --- a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js +++ b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js @@ -13,6 +13,7 @@ import BlockTypesList from '../block-types-list'; import InserterPanel from './panel'; import InserterNoResults from './no-results'; import useBlockTypesState from './hooks/use-block-types-state'; +import ReusableBlocksRenameHint from './reusable-block-rename-hint'; function ReusableBlocksList( { onHover, onInsert, rootClientId } ) { const [ items, , , onSelectItem ] = useBlockTypesState( @@ -54,6 +55,9 @@ function ReusableBlocksList( { onHover, onInsert, rootClientId } ) { export function ReusableBlocksTab( { rootClientId, onInsert, onHover } ) { return ( <> +
+ +
} - { Children.count( children ) > 0 && } ); } diff --git a/packages/block-editor/src/components/list-view/use-list-view-client-ids.js b/packages/block-editor/src/components/list-view/use-list-view-client-ids.js index d51412fdf2c3db..8a1ccfcede4c12 100644 --- a/packages/block-editor/src/components/list-view/use-list-view-client-ids.js +++ b/packages/block-editor/src/components/list-view/use-list-view-client-ids.js @@ -16,14 +16,14 @@ export default function useListViewClientIds( { blocks, rootClientId } ) { const { getDraggedBlockClientIds, getSelectedBlockClientIds, - getListViewClientIdsTree, + getEnabledClientIdsTree, } = unlock( select( blockEditorStore ) ); return { selectedClientIds: getSelectedBlockClientIds(), draggedClientIds: getDraggedBlockClientIds(), clientIdsTree: - blocks ?? getListViewClientIdsTree( rootClientId ), + blocks ?? getEnabledClientIdsTree( rootClientId ), }; }, [ blocks, rootClientId ] diff --git a/packages/block-editor/src/components/panel-color-settings/README.md b/packages/block-editor/src/components/panel-color-settings/README.md new file mode 100644 index 00000000000000..94b82eb3869b1a --- /dev/null +++ b/packages/block-editor/src/components/panel-color-settings/README.md @@ -0,0 +1,98 @@ +# PanelColorSettings + +`PanelColorSettings` is a React component that renders a UI for managing various color settings. +It is essentially a wrapper around the `PanelColorGradientSettings` component, but specifically disables the gradient features. + +## Usage + +```jsx +/** + * WordPress dependencies + */ +import { PanelColorSettings } from '@wordpress/block-editor'; +import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +// ... + +const MyPanelColorSettings = () => { + const [ textColor, setTextColor ] = useState( { color: '#000' } ); + const [ backgroundColor, setBackgroundColor ] = useState( { + color: '#fff', + } ); + const [ overlayTextColor, setOverlayTextColor ] = useState( { + color: '#000', + } ); + const [ overlayBackgroundColor, setOverlayBackgroundColor ] = useState( { + color: '#eee', + } ); + + return ( + + ); +}; + +/// ... + +; +``` + +## Props + +The component accepts the following props: + +### colorSettings + +A user-provided set of color settings. + +- Type: `Array` +- Required: No + +Colors settings are provided as an array of objects with the following schema: + +| Property | Description | Type | +| -------- | --------------------------------- | -------- | +| value | The current color of the setting | string | +| onChange | Callback on change of the setting | Function | +| label | Label of the setting | string | + +Additionally, the following `PanelColorGradientSettings` props are supported and directly passed down to the underlying `PanelColorGradientSettings` instance: + +- `className` - added to the underlying `ToolsPanel` instance. +- `colors` - array of colors to be used. +- `gradients` - not recommended to be used since `PanelColorSettings` resets it. +- `disableCustomColors` - whether addition of custom colors is enabled +- `disableCustomGradients` - not recommended to be used since `PanelColorSettings` sets it. +- `children` - displayed below the underlying `PanelColorGradientSettings` instance. +- `settings` - not recommended to be used, since `PanelColorSettings` builds it from the `colorSettings` prop. +- `title` - title of the underlying `ToolsPanel`. +- `showTitle` - whether to show the title of the `ToolsPanel`. +- `__experimentalIsRenderedInSidebar` +- `enableAlpha` - whether to enable setting opacity when specifying a color. + +Please refer to the `PanelColorGradientSettings` component for more information. diff --git a/packages/block-editor/src/components/preview-options/README.md b/packages/block-editor/src/components/preview-options/README.md index 0a2e89a70c7d44..6e9a029fa83a57 100644 --- a/packages/block-editor/src/components/preview-options/README.md +++ b/packages/block-editor/src/components/preview-options/README.md @@ -28,23 +28,24 @@ const MyPreviewOptions = () => ( className="edit-post-post-preview-dropdown" deviceType={ deviceType } setDeviceType={ setPreviewDeviceType } - > - -
- - { __( 'Preview in new tab' ) } - - - } - /> -
-
+ > { ( { onClose } ) => ( + +
+ + { __( 'Preview in new tab' ) } + + + } + onPreview={ onClose } + /> +
+
+ ) } ); ``` diff --git a/packages/block-editor/src/components/preview-options/index.js b/packages/block-editor/src/components/preview-options/index.js index c9f9a6ff782e4f..c22109e7359d1d 100644 --- a/packages/block-editor/src/components/preview-options/index.js +++ b/packages/block-editor/src/components/preview-options/index.js @@ -54,7 +54,7 @@ export default function PreviewOptions( { icon={ deviceIcons[ deviceType.toLowerCase() ] } label={ label || __( 'Preview' ) } > - { () => ( + { ( renderProps ) => ( <> - { children } + { children( renderProps ) } ) } diff --git a/packages/block-editor/src/components/recursion-provider/README.md b/packages/block-editor/src/components/recursion-provider/README.md new file mode 100644 index 00000000000000..4538fd6a7d3507 --- /dev/null +++ b/packages/block-editor/src/components/recursion-provider/README.md @@ -0,0 +1,101 @@ +# RecursionProvider + +According to Gutenberg's block rendering architecture, any block type capable of recursion should be responsible for handling its own infinite loops. + +To help with detecting infinite loops on the client, the `RecursionProvider` component and the `useHasRecursion()` hook are used to identify if a block has already been rendered. + +## Usage + +```jsx +/** + * WordPress dependencies + */ +import { + __experimentalRecursionProvider as RecursionProvider, + __experimentalUseHasRecursion as useHasRecursion, + useBlockProps, + Warning, +} from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +export default function MyRecursiveBlockEdit( { attributes: { ref } } ) { + const hasAlreadyRendered = useHasRecursion( ref ); + const blockProps = useBlockProps( { + className: 'my-block__custom-class', + } ); + + if ( hasAlreadyRendered ) { + return ( +
+ + { __( 'Block cannot be rendered inside itself.' ) } + +
+ ); + } + + return ( + + Block editing code here.... + + ); +} + +/// ... + +; +``` + +## Props + +The component accepts the following props: + +### uniqueId + +Any value that acts as a unique identifier for a block instance. + +- Type: `any` +- Required: Yes + +### children + +Components to be rendered as content. + +- Type: `Element` +- Required: Yes. + +### blockName + +Optional block name. + +- Type: `String` +- Required: No +- Default: '' + +# `useHasRecursion()` + +Used in conjunction with `RecursionProvider`, this hook is used to identify if a block has already been rendered. + +## Usage + +For example usage, refer to the example above. + +## Props + +The component accepts the following props: + +### uniqueId + +Any value that acts as a unique identifier for a block instance. + +- Type: `any` +- Required: Yes + +### blockName + +Optional block name. + +- Type: `String` +- Required: No +- Default: '' + diff --git a/packages/block-editor/src/components/rich-text/content.js b/packages/block-editor/src/components/rich-text/content.js index dfd206a1ddb7e9..9762582f86f141 100644 --- a/packages/block-editor/src/components/rich-text/content.js +++ b/packages/block-editor/src/components/rich-text/content.js @@ -2,11 +2,7 @@ * WordPress dependencies */ import { RawHTML } from '@wordpress/element'; -import { - children as childrenSource, - getSaveElement, - __unstableGetBlockProps as getBlockProps, -} from '@wordpress/blocks'; +import { children as childrenSource } from '@wordpress/blocks'; import deprecated from '@wordpress/deprecated'; /** @@ -42,44 +38,3 @@ export const Content = ( { value, tagName: Tag, multiline, ...props } ) => { return content; }; - -Content.__unstableIsRichTextContent = {}; - -function findContent( blocks, richTextValues = [] ) { - if ( ! Array.isArray( blocks ) ) { - blocks = [ blocks ]; - } - - for ( const block of blocks ) { - if ( - block?.type?.__unstableIsRichTextContent === - Content.__unstableIsRichTextContent - ) { - richTextValues.push( block.props.value ); - continue; - } - - if ( block?.props?.children ) { - findContent( block.props.children, richTextValues ); - } - } - - return richTextValues; -} - -function _getSaveElement( { name, attributes, innerBlocks } ) { - return getSaveElement( - name, - attributes, - innerBlocks.map( _getSaveElement ) - ); -} - -export function getRichTextValues( blocks = [] ) { - getBlockProps.skipFilters = true; - const values = findContent( - ( Array.isArray( blocks ) ? blocks : [ blocks ] ).map( _getSaveElement ) - ); - getBlockProps.skipFilters = false; - return values; -} diff --git a/packages/block-editor/src/components/rich-text/get-rich-text-values.js b/packages/block-editor/src/components/rich-text/get-rich-text-values.js new file mode 100644 index 00000000000000..4ecee9b76530e5 --- /dev/null +++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js @@ -0,0 +1,95 @@ +/** + * WordPress dependencies + */ +import { RawHTML, StrictMode, Fragment } from '@wordpress/element'; +import { + getSaveElement, + __unstableGetBlockProps as getBlockProps, +} from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import InnerBlocks from '../inner-blocks'; +import { Content } from './content'; + +/* + * This function is similar to `@wordpress/element`'s `renderToString` function, + * except that it does not render the elements to a string, but instead collects + * the values of all rich text `Content` elements. + */ +function addValuesForElement( element, ...args ) { + if ( null === element || undefined === element || false === element ) { + return; + } + + if ( Array.isArray( element ) ) { + return addValuesForElements( element, ...args ); + } + + switch ( typeof element ) { + case 'string': + case 'number': + return; + } + + const { type, props } = element; + + switch ( type ) { + case StrictMode: + case Fragment: + return addValuesForElements( props.children, ...args ); + case RawHTML: + return; + case InnerBlocks.Content: + return addValuesForBlocks( ...args ); + case Content: + const [ values ] = args; + values.push( props.value ); + return; + } + + switch ( typeof type ) { + case 'string': + if ( typeof props.children !== 'undefined' ) { + return addValuesForElements( props.children, ...args ); + } + return; + case 'function': + if ( + type.prototype && + typeof type.prototype.render === 'function' + ) { + return addValuesForElement( + new type( props ).render(), + ...args + ); + } + + return addValuesForElement( type( props ), ...args ); + } +} + +function addValuesForElements( children, ...args ) { + children = Array.isArray( children ) ? children : [ children ]; + + for ( let i = 0; i < children.length; i++ ) { + addValuesForElement( children[ i ], ...args ); + } +} + +function addValuesForBlocks( values, blocks ) { + for ( let i = 0; i < blocks.length; i++ ) { + const { name, attributes, innerBlocks } = blocks[ i ]; + const saveElement = getSaveElement( name, attributes ); + addValuesForElement( saveElement, values, innerBlocks ); + } +} + +export function getRichTextValues( blocks = [] ) { + getBlockProps.skipFilters = true; + const values = []; + addValuesForBlocks( values, blocks ); + getBlockProps.skipFilters = false; + return values; +} diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.native.js b/packages/block-editor/src/components/use-block-drop-zone/index.native.js index 5a64803aa4bc20..4f00880873c2fa 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.native.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.native.js @@ -1,14 +1,17 @@ /** * External dependencies */ -import { useSharedValue } from 'react-native-reanimated'; +import { + runOnJS, + useDerivedValue, + useSharedValue, +} from 'react-native-reanimated'; /** * WordPress dependencies */ import { useSelect } from '@wordpress/data'; import { useCallback } from '@wordpress/element'; -import { useThrottle } from '@wordpress/compose'; /** * Internal dependencies @@ -18,6 +21,8 @@ import { useBlockListContext } from '../block-list/block-list-context'; import { getDistanceToNearestEdge } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; +const UPDATE_TARGET_BLOCK_INDEX_THRESHOLD = 20; // In pixels + /** @typedef {import('../../utils/math').WPPoint} WPPoint */ /** @@ -111,6 +116,14 @@ export default function useBlockDropZone( { rootClientId: targetRootClientId = '', } = {} ) { const targetBlockIndex = useSharedValue( null ); + const dragPosition = { + x: useSharedValue( 0 ), + y: useSharedValue( 0 ), + }; + const prevDragPosition = { + x: useSharedValue( 0 ), + y: useSharedValue( 0 ), + }; const { getBlockListSettings, getSettings } = useSelect( blockEditorStore ); const { blocksLayouts, getBlockLayoutsOrderedByYCoord } = @@ -118,43 +131,67 @@ export default function useBlockDropZone( { const getSortedBlocksLayouts = useCallback( () => { return getBlockLayoutsOrderedByYCoord( blocksLayouts.current ); + // We use the value of `blocksLayouts` as the dependency. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ blocksLayouts.current ] ); const isRTL = getSettings().isRTL; const onBlockDrop = useOnBlockDrop(); - const throttled = useThrottle( - useCallback( - ( event ) => { - const sortedBlockLayouts = getSortedBlocksLayouts(); - - const targetIndex = getNearestBlockIndex( - sortedBlockLayouts, - { x: event.x, y: event.y }, - getBlockListSettings( targetRootClientId )?.orientation, - isRTL - ); - if ( targetIndex !== null ) { - targetBlockIndex.value = targetIndex ?? 0; - } - }, - [ - getSortedBlocksLayouts, - getNearestBlockIndex, - getBlockListSettings, - targetBlockIndex, - ] - ), - 200 + const updateTargetBlockIndex = useCallback( + ( event ) => { + const sortedBlockLayouts = getSortedBlocksLayouts(); + + const targetIndex = getNearestBlockIndex( + sortedBlockLayouts, + { x: event.x, y: event.y }, + getBlockListSettings( targetRootClientId )?.orientation, + isRTL + ); + if ( targetIndex !== null ) { + targetBlockIndex.value = targetIndex ?? 0; + } + }, + [ + getSortedBlocksLayouts, + getBlockListSettings, + targetRootClientId, + isRTL, + targetBlockIndex, + ] ); + useDerivedValue( () => { + const x = dragPosition.x.value; + const y = dragPosition.y.value; + const prevX = prevDragPosition.x.value; + const prevY = prevDragPosition.y.value; + // `updateTargetBlockIndex` performs expensive calculations, so we throttle + // the call using a offset threshold based on the dragging position. + if ( + Math.abs( x - prevX ) >= UPDATE_TARGET_BLOCK_INDEX_THRESHOLD || + Math.abs( y - prevY ) >= UPDATE_TARGET_BLOCK_INDEX_THRESHOLD + ) { + runOnJS( updateTargetBlockIndex )( { x, y } ); + prevDragPosition.x.value = x; + prevDragPosition.y.value = y; + return true; + } + return false; + } ); + return { - onBlockDragOver( event ) { - throttled( event ); + onBlockDragOver( { x, y } ) { + dragPosition.x.value = x; + dragPosition.y.value = y; + }, + onBlockDragOverWorklet( { x, y } ) { + 'worklet'; + dragPosition.x.value = x; + dragPosition.y.value = y; }, onBlockDragEnd() { - throttled.cancel(); targetBlockIndex.value = null; }, onBlockDrop: ( event ) => { diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 1200dee367d243..453fbd7ce63eb9 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -4,7 +4,7 @@ import * as globalStyles from './components/global-styles'; import { ExperimentalBlockEditorProvider } from './components/provider'; import { lock } from './lock-unlock'; -import { getRichTextValues } from './components/rich-text/content'; +import { getRichTextValues } from './components/rich-text/get-rich-text-values'; import { kebabCase } from './utils/object'; import ResizableBoxPopover from './components/resizable-box-popover'; import { ComposedPrivateInserter as PrivateInserter } from './components/inserter'; diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 9dd3b24009c512..1d7ec460fe8d9e 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -28,7 +28,6 @@ import { } from '../utils/selection'; import { __experimentalUpdateSettings, - ensureDefaultBlock, privateRemoveBlocks, } from './private-actions'; @@ -403,7 +402,7 @@ export const replaceBlocks = initialPosition, meta, } ); - dispatch( ensureDefaultBlock() ); + dispatch.ensureDefaultBlock(); }; /** diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 3abc1b0b3bdfd3..e6fb17c5f6e3c7 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -136,21 +136,18 @@ export const isBlockSubtreeDisabled = createSelector( * * @return {Object[]} Tree of block objects with only clientID and innerBlocks set. */ -export const getListViewClientIdsTree = createSelector( +export const getEnabledClientIdsTree = createSelector( ( state, rootClientId = '' ) => { return getBlockOrder( state, rootClientId ).flatMap( ( clientId ) => { if ( getBlockEditingMode( state, clientId ) !== 'disabled' ) { return [ { clientId, - innerBlocks: getListViewClientIdsTree( - state, - clientId - ), + innerBlocks: getEnabledClientIdsTree( state, clientId ), }, ]; } - return getListViewClientIdsTree( state, clientId ); + return getEnabledClientIdsTree( state, clientId ); } ); }, ( state ) => [ diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index fc314636d11951..4cca99535a8e50 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2034,11 +2034,13 @@ export const getInserterItems = createSelector( ? getReusableBlocks( state ) .filter( ( reusableBlock ) => - // Filter to either fully synced patterns (sync_status === 'fully'), - // or old school reusable blocks (sync_status === ''). - reusableBlock.meta?.sync_status === 'fully' || - reusableBlock.meta?.sync_status === '' || - ! reusableBlock.meta?.sync_status + // Reusable blocks that are fully synced should have no sync status set + // for backwards compat between patterns and old reusable blocks, but + // some in release 16.1 may have had sync status inadvertantly set to + // 'fully' if created in the site editor. + reusableBlock.wp_pattern_sync_status === 'fully' || + reusableBlock.wp_pattern_sync_status === '' || + ! reusableBlock.wp_pattern_sync_status ) .map( buildReusableBlockInserterItem ) : []; @@ -2313,7 +2315,8 @@ function getUnsyncedPatterns( state ) { return reusableBlocks .filter( - ( reusableBlock ) => reusableBlock.meta?.sync_status === 'unsynced' + ( reusableBlock ) => + reusableBlock.wp_pattern_sync_status === 'unsynced' ) .map( ( reusableBlock ) => { return { diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index fc15737eda8d2f..48fc234e0e6c21 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -216,6 +216,7 @@ describe( 'actions', () => { getBlockCount: () => 1, }; const dispatch = jest.fn(); + dispatch.ensureDefaultBlock = jest.fn(); replaceBlock( 'chicken', block )( { select, dispatch } ); @@ -281,6 +282,7 @@ describe( 'actions', () => { getBlockCount: () => 1, }; const dispatch = jest.fn(); + dispatch.ensureDefaultBlock = jest.fn(); replaceBlocks( [ 'chicken' ], blocks )( { select, dispatch } ); @@ -314,6 +316,7 @@ describe( 'actions', () => { getBlockCount: () => 1, }; const dispatch = jest.fn(); + dispatch.ensureDefaultBlock = jest.fn(); replaceBlocks( [ 'chicken' ], diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index 30cf702c605263..e826db4a62bb9d 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -11,7 +11,7 @@ import { getLastInsertedBlocksClientIds, getBlockEditingMode, isBlockSubtreeDisabled, - getListViewClientIdsTree, + getEnabledClientIdsTree, getEnabledBlockParents, } from '../private-selectors'; @@ -391,7 +391,7 @@ describe( 'private selectors', () => { } ); } ); - describe( 'getListViewClientIdsTree', () => { + describe( 'getEnabledClientIdsTree', () => { const baseState = { settings: {}, blocks: { @@ -462,7 +462,7 @@ describe( 'private selectors', () => { ...baseState, blockEditingModes: new Map( [] ), }; - expect( getListViewClientIdsTree( state ) ).toEqual( [ + expect( getEnabledClientIdsTree( state ) ).toEqual( [ { clientId: '6cf70164-9097-4460-bcbf-200560546988', innerBlocks: [], @@ -500,7 +500,7 @@ describe( 'private selectors', () => { blockEditingModes: new Map( [] ), }; expect( - getListViewClientIdsTree( + getEnabledClientIdsTree( state, 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337' ) @@ -534,7 +534,7 @@ describe( 'private selectors', () => { [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', 'contentOnly' ], ] ), }; - expect( getListViewClientIdsTree( state ) ).toEqual( [ + expect( getEnabledClientIdsTree( state ) ).toEqual( [ { clientId: 'b26fc763-417d-4f01-b81c-2ec61e14a972', innerBlocks: [], diff --git a/packages/block-editor/src/utils/object.js b/packages/block-editor/src/utils/object.js index a543c1e4f1d301..a144f1b1bf53a9 100644 --- a/packages/block-editor/src/utils/object.js +++ b/packages/block-editor/src/utils/object.js @@ -56,12 +56,17 @@ export function kebabCase( str ) { /** * Clones an object. + * Arrays are also cloned as arrays. * Non-object values are returned unchanged. * * @param {*} object Object to clone. * @return {*} Cloned object, or original literal non-object value. */ function cloneObject( object ) { + if ( Array.isArray( object ) ) { + return object.map( cloneObject ); + } + if ( object && typeof object === 'object' ) { return { ...Object.fromEntries( @@ -79,7 +84,7 @@ function cloneObject( object ) { /** * Immutably sets a value inside an object. Like `lodash#set`, but returning a * new object. Treats nullish initial values as empty objects. Clones any - * nested objects. + * nested objects. Supports arrays, too. * * @param {Object} object Object to set a value in. * @param {number|string|Array} path Path in the object to modify. @@ -92,7 +97,11 @@ export function setImmutably( object, path, value ) { normalizedPath.reduce( ( acc, key, i ) => { if ( acc[ key ] === undefined ) { - acc[ key ] = {}; + if ( Number.isInteger( path[ i + 1 ] ) ) { + acc[ key ] = []; + } else { + acc[ key ] = {}; + } } if ( i === normalizedPath.length - 1 ) { acc[ key ] = value; diff --git a/packages/block-editor/src/utils/test/object.js b/packages/block-editor/src/utils/test/object.js index def7e5e9c8f057..87f01375df311d 100644 --- a/packages/block-editor/src/utils/test/object.js +++ b/packages/block-editor/src/utils/test/object.js @@ -150,6 +150,22 @@ describe( 'setImmutably', () => { expect( result ).toEqual( { test: 2 } ); } ); + it( 'handles first level arrays properly', () => { + const result = setImmutably( [ 5 ], 0, 6 ); + + expect( result ).toEqual( [ 6 ] ); + } ); + + it( 'handles nested arrays properly', () => { + const result = setImmutably( + [ [ 'foo', [ 'bar' ] ] ], + [ 0, 1, 0 ], + 'baz' + ); + + expect( result ).toEqual( [ [ 'foo', [ 'baz' ] ] ] ); + } ); + describe( 'with array notation access', () => { it( 'assigns values at deeper levels', () => { const result = setImmutably( {}, [ 'foo', 'bar', 'baz' ], 5 ); @@ -236,5 +252,25 @@ describe( 'setImmutably', () => { expect( result.foo.bar ).not.toBe( input.foo.bar ); expect( result.foo.bar.baz ).not.toBe( input.foo.bar.baz ); } ); + + it( 'clones arrays at the first level', () => { + const input = []; + const result = setImmutably( input, 0, 1 ); + + expect( result ).not.toBe( input ); + } ); + + it( 'clones arrays at deeper levels', () => { + const input = [ [ [ [ 'foo', [ 'bar' ] ] ] ] ]; + const result = setImmutably( input, [ 0, 0, 0, 1, 0 ], 'baz' ); + + expect( result ).not.toBe( input ); + expect( result[ 0 ] ).not.toBe( input[ 0 ] ); + expect( result[ 0 ][ 0 ] ).not.toBe( input[ 0 ][ 0 ] ); + expect( result[ 0 ][ 0 ][ 0 ] ).not.toBe( input[ 0 ][ 0 ][ 0 ] ); + expect( result[ 0 ][ 0 ][ 0 ][ 1 ] ).not.toBe( + input[ 0 ][ 0 ][ 0 ][ 1 ] + ); + } ); } ); } ); diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 12d3e9ce980be5..fa26d4a346cebd 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 8.14.0 (2023-07-05) + ## 8.13.0 (2023-06-23) ## 8.12.0 (2023-06-07) diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 213d6690f7afe8..a5eebe6bbc9d0e 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "8.13.0", + "version": "8.14.0", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -31,7 +31,6 @@ ], "dependencies": { "@babel/runtime": "^7.16.0", - "@preact/signals": "^1.1.3", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/autop": "file:../autop", @@ -65,13 +64,10 @@ "change-case": "^4.1.2", "classnames": "^2.3.1", "colord": "^2.7.0", - "deepsignal": "^1.3.0", "escape-html": "^1.0.3", "fast-average-color": "^9.1.1", "fast-deep-equal": "^3.1.3", "memize": "^2.1.0", - "micromodal": "^0.4.10", - "preact": "^10.13.2", "remove-accents": "^0.4.2", "uuid": "^8.3.0" }, diff --git a/packages/block-library/src/archives/block.json b/packages/block-library/src/archives/block.json index 4d30b5344d2a68..7e0f5181d2c3dd 100644 --- a/packages/block-library/src/archives/block.json +++ b/packages/block-library/src/archives/block.json @@ -29,7 +29,11 @@ "html": false, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/audio/block.json b/packages/block-library/src/audio/block.json index adcf67e4dc10cc..a4740e304451ce 100644 --- a/packages/block-library/src/audio/block.json +++ b/packages/block-library/src/audio/block.json @@ -49,7 +49,11 @@ "align": true, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } } }, "editorStyle": "wp-block-audio-editor", diff --git a/packages/block-library/src/categories/block.json b/packages/block-library/src/categories/block.json index e0d77ff5696ec5..5014da82980493 100644 --- a/packages/block-library/src/categories/block.json +++ b/packages/block-library/src/categories/block.json @@ -33,7 +33,11 @@ "html": false, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/code/block.json b/packages/block-library/src/code/block.json index 7f58e79d221759..4d19423f1b6291 100644 --- a/packages/block-library/src/code/block.json +++ b/packages/block-library/src/code/block.json @@ -31,7 +31,11 @@ }, "spacing": { "margin": [ "top", "bottom" ], - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "__experimentalBorder": { "radius": true, diff --git a/packages/block-library/src/column/edit.native.js b/packages/block-library/src/column/edit.native.js index e5ad6c7827d236..46e5012f68a345 100644 --- a/packages/block-library/src/column/edit.native.js +++ b/packages/block-library/src/column/edit.native.js @@ -42,7 +42,6 @@ function ColumnEdit( { hasChildren, isSelected, getStylesFromColorScheme, - isParentSelected, contentStyle, columns, selectedColumnIndex, @@ -140,12 +139,10 @@ function ColumnEdit( { return ( @@ -258,8 +255,6 @@ export default compose( [ const parentId = getBlockRootClientId( clientId ); const hasChildren = !! getBlockCount( clientId ); - const isParentSelected = - selectedBlockClientId && selectedBlockClientId === parentId; const blockOrder = getBlockOrder( parentId ); @@ -271,7 +266,6 @@ export default compose( [ return { hasChildren, - isParentSelected, isSelected, selectedColumnIndex, columns, diff --git a/packages/block-library/src/column/editor.native.scss b/packages/block-library/src/column/editor.native.scss index ef937134a43c3f..f76d85ff44b235 100644 --- a/packages/block-library/src/column/editor.native.scss +++ b/packages/block-library/src/column/editor.native.scss @@ -1,7 +1,3 @@ -.columnPlaceholderNotSelected { - padding-top: $block-selected-to-content; -} - .columnPlaceholder { flex: 1; padding: $block-selected-to-content; diff --git a/packages/block-library/src/comment-template/index.php b/packages/block-library/src/comment-template/index.php index 3a553e802de0e7..bb1cfa474e4c36 100644 --- a/packages/block-library/src/comment-template/index.php +++ b/packages/block-library/src/comment-template/index.php @@ -35,8 +35,11 @@ function block_core_comment_template_render_comments( $comments, $block ) { * We set commentId context through the `render_block_context` filter so * that dynamically inserted blocks (at `render_block` filter stage) * will also receive that context. + * + * Use an early priority to so that other 'render_block_context' filters + * have access to the values. */ - add_filter( 'render_block_context', $filter_block_context ); + add_filter( 'render_block_context', $filter_block_context, 1 ); /* * We construct a new WP_Block instance from the parsed block so that @@ -44,7 +47,7 @@ function block_core_comment_template_render_comments( $comments, $block ) { */ $block_content = ( new WP_Block( $block->parsed_block ) )->render( array( 'dynamic' => false ) ); - remove_filter( 'render_block_context', $filter_block_context ); + remove_filter( 'render_block_context', $filter_block_context, 1 ); $children = $comment->get_children(); diff --git a/packages/block-library/src/details/block.json b/packages/block-library/src/details/block.json index 01110dfec26ff2..222be7357c3fb3 100644 --- a/packages/block-library/src/details/block.json +++ b/packages/block-library/src/details/block.json @@ -34,7 +34,11 @@ "html": false, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/footnotes/block.json b/packages/block-library/src/footnotes/block.json index 0ab992009d123f..e021e9c5225dab 100644 --- a/packages/block-library/src/footnotes/block.json +++ b/packages/block-library/src/footnotes/block.json @@ -11,7 +11,6 @@ "supports": { "html": false, "multiple": false, - "inserter": false, "reusable": false }, "style": "wp-block-footnotes" diff --git a/packages/block-library/src/footnotes/edit.js b/packages/block-library/src/footnotes/edit.js index e90a7f82be94a9..fdfe7a94039af9 100644 --- a/packages/block-library/src/footnotes/edit.js +++ b/packages/block-library/src/footnotes/edit.js @@ -1,8 +1,11 @@ /** * WordPress dependencies */ -import { RichText, useBlockProps } from '@wordpress/block-editor'; +import { BlockIcon, RichText, useBlockProps } from '@wordpress/block-editor'; import { useEntityProp } from '@wordpress/core-data'; +import { __ } from '@wordpress/i18n'; +import { Placeholder } from '@wordpress/components'; +import { formatListNumbered as icon } from '@wordpress/icons'; export default function FootnotesEdit( { context: { postType, postId } } ) { const [ meta, updateMeta ] = useEntityProp( @@ -12,8 +15,24 @@ export default function FootnotesEdit( { context: { postType, postId } } ) { postId ); const footnotes = meta?.footnotes ? JSON.parse( meta.footnotes ) : []; + const blockProps = useBlockProps(); + + if ( ! footnotes.length ) { + return ( +
+ } + label={ __( 'Footnotes' ) } + instructions={ __( + 'Footnotes found in blocks within this document will be displayed here.' + ) } + /> +
+ ); + } + return ( -
    +
      { footnotes.map( ( { id, content } ) => (
    1. *`, }, value.end, value.end diff --git a/packages/block-library/src/footnotes/style.scss b/packages/block-library/src/footnotes/style.scss index 4debba0560f173..aa7ab8b6951dd3 100644 --- a/packages/block-library/src/footnotes/style.scss +++ b/packages/block-library/src/footnotes/style.scss @@ -1,17 +1,20 @@ +// These styles are for backwards compatibility with the old footnotes anchors. +// Can be removed in the future. .editor-styles-wrapper, .entry-content { counter-reset: footnotes; } -[data-fn].fn { +a[data-fn].fn { vertical-align: super; font-size: smaller; counter-increment: footnotes; - display: inline-block; + display: inline-flex; + text-decoration: none; text-indent: -9999999px; } -[data-fn].fn::after { +a[data-fn].fn::after { content: "[" counter(footnotes) "]"; text-indent: 0; float: left; diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json index 07425e1037402d..69427d53dfef20 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -115,7 +115,9 @@ "blockGap": [ "horizontal", "vertical" ], "__experimentalSkipSerialization": [ "blockGap" ], "__experimentalDefaultControls": { - "blockGap": true + "blockGap": true, + "margin": false, + "padding": false } }, "color": { diff --git a/packages/block-library/src/gallery/test/index.native.js b/packages/block-library/src/gallery/test/index.native.js index a64b8bf4032811..ef4f445db337bc 100644 --- a/packages/block-library/src/gallery/test/index.native.js +++ b/packages/block-library/src/gallery/test/index.native.js @@ -161,7 +161,7 @@ describe( 'Gallery block', () => { // This case is disabled until the issue (https://github.com/WordPress/gutenberg/issues/38444) // is addressed. - it.skip( 'block remains selected after dimissing the media options picker', async () => { + it.skip( 'block remains selected after dismissing the media options picker', async () => { // Initialize with an empty gallery const { getByLabelText, getByText, getByTestId } = await initializeEditor( { @@ -175,7 +175,7 @@ describe( 'Gallery block', () => { expect( getByText( 'Choose images' ) ).toBeVisible(); expect( getByText( 'WordPress Media Library' ) ).toBeVisible(); - // Dimiss the picker + // Dismiss the picker if ( Platform.isIOS ) { fireEvent.press( getByText( 'Cancel' ) ); } else { @@ -511,10 +511,11 @@ describe( 'Gallery block', () => { // Reference: https://github.com/wordpress-mobile/test-cases/blob/trunk/test-cases/gutenberg/gallery.md#tc010 it( 'rearranges gallery items', async () => { // Initialize with a gallery that contains three items - const { galleryBlock } = await initializeWithGalleryBlock( { - numberOfItems: 3, - media, - } ); + const { getByLabelText, galleryBlock } = + await initializeWithGalleryBlock( { + numberOfItems: 3, + media, + } ); // Rearrange items (final disposition will be: Image 3 - Image 1 - Image 2) const galleryItem1 = getGalleryItem( galleryBlock, 1 ); @@ -523,7 +524,7 @@ describe( 'Gallery block', () => { fireEvent.press( galleryItem3 ); await act( () => fireEvent.press( - within( galleryItem3 ).getByLabelText( + getByLabelText( /Move block left from position 3 to position 2/ ) ) @@ -532,7 +533,7 @@ describe( 'Gallery block', () => { fireEvent.press( galleryItem1 ); await act( () => fireEvent.press( - within( galleryItem1 ).getByLabelText( + getByLabelText( /Move block right from position 1 to position 2/ ) ) diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json index ff2a5d267511b4..80f1f0c47b5a8f 100644 --- a/packages/block-library/src/heading/block.json +++ b/packages/block-library/src/heading/block.json @@ -40,7 +40,11 @@ }, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/image/deprecated.js b/packages/block-library/src/image/deprecated.js index 34b4573c1002cb..bdfdca6ee3c4d6 100644 --- a/packages/block-library/src/image/deprecated.js +++ b/packages/block-library/src/image/deprecated.js @@ -6,7 +6,11 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { RichText, useBlockProps } from '@wordpress/block-editor'; +import { + RichText, + useBlockProps, + __experimentalGetElementClassName as getBorderClassesAndStyles, +} from '@wordpress/block-editor'; /** * Deprecation for adding the `wp-image-${id}` class to the image block for @@ -539,4 +543,95 @@ const v5 = { }, }; -export default [ v5, v4, v3, v2, v1 ]; +/** + * Deprecation for adding width and height as style rules on the inner img. + * It also updates the widht and height attributes to be strings instead of numbers. + * + * @see https://github.com/WordPress/gutenberg/pull/31366 + */ +const v6 = { + save( { attributes } ) { + const { + url, + alt, + caption, + align, + href, + rel, + linkClass, + width, + height, + aspectRatio, + scale, + id, + linkTarget, + sizeSlug, + title, + } = attributes; + + const newRel = ! rel ? undefined : rel; + const borderProps = getBorderClassesAndStyles( attributes ); + + const classes = classnames( { + [ `align${ align }` ]: align, + [ `size-${ sizeSlug }` ]: sizeSlug, + 'is-resized': width || height, + 'has-custom-border': + !! borderProps.className || + ( borderProps.style && + Object.keys( borderProps.style ).length > 0 ), + } ); + + const imageClasses = classnames( borderProps.className, { + [ `wp-image-${ id }` ]: !! id, + } ); + + const image = ( + { + ); + + const figure = ( + <> + { href ? ( + + { image } + + ) : ( + image + ) } + { ! RichText.isEmpty( caption ) && ( + + ) } + + ); + + return ( +
      + { figure } +
      + ); + }, +}; + +export default [ v6, v5, v4, v3, v2, v1 ]; diff --git a/packages/block-library/src/image/save.js b/packages/block-library/src/image/save.js index 95e8803dd67858..6fa8c6b2342f32 100644 --- a/packages/block-library/src/image/save.js +++ b/packages/block-library/src/image/save.js @@ -58,6 +58,8 @@ export default function save( { attributes } ) { ...borderProps.style, aspectRatio, objectFit: scale, + width, + height, } } width={ width } height={ height } diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 2b4a0f2fb95f2a..911bd0c37451ee 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -142,7 +142,6 @@ const getAllBlocks = () => { buttons, calendar, categories, - ...( window.wp && window.wp.oldEditor ? [ classic ] : [] ), // Only add the classic block in WP Context. code, column, columns, @@ -229,6 +228,24 @@ const getAllBlocks = () => { queryTitle, postAuthorBiography, ]; + + // When in a WordPress context, conditionally + // add the classic block and TinyMCE editor + // under any of the following conditions: + // - the current post contains a classic block + // - the experiment to disable TinyMCE isn't active. + // - a query argument specifies that TinyMCE should be loaded + if ( + window?.wp?.oldEditor && + ( window?.wp?.needsClassicBlock || + ! window?.__experimentalDisableTinymce || + !! new URLSearchParams( window?.location?.search ).get( + 'requiresTinymce' + ) ) + ) { + blocks.push( classic ); + } + return blocks.filter( Boolean ); }; diff --git a/packages/block-library/src/list/block.json b/packages/block-library/src/list/block.json index e7ef850d29ba55..9c4c1ef4f27848 100644 --- a/packages/block-library/src/list/block.json +++ b/packages/block-library/src/list/block.json @@ -61,7 +61,11 @@ }, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "__unstablePasteTextInline": true, "__experimentalSelector": "ol,ul", diff --git a/packages/block-library/src/list/edit.js b/packages/block-library/src/list/edit.js index 24d5ead74c47d4..7c8c15e05fe875 100644 --- a/packages/block-library/src/list/edit.js +++ b/packages/block-library/src/list/edit.js @@ -177,10 +177,12 @@ export default function Edit( { attributes, setAttributes, clientId, style } ) { { controls } { ordered && ( ) } diff --git a/packages/block-library/src/missing/edit.js b/packages/block-library/src/missing/edit.js index 1ef143a639ed06..f7aef453b5447c 100644 --- a/packages/block-library/src/missing/edit.js +++ b/packages/block-library/src/missing/edit.js @@ -16,22 +16,46 @@ import { safeHTML } from '@wordpress/dom'; function MissingBlockWarning( { attributes, convertToHTML, clientId } ) { const { originalName, originalUndelimitedContent } = attributes; const hasContent = !! originalUndelimitedContent; - const hasHTMLBlock = useSelect( + const { hasFreeformBlock, hasHTMLBlock } = useSelect( ( select ) => { const { canInsertBlockType, getBlockRootClientId } = select( blockEditorStore ); - return canInsertBlockType( - 'core/html', - getBlockRootClientId( clientId ) - ); + return { + hasFreeformBlock: canInsertBlockType( + 'core/freeform', + getBlockRootClientId( clientId ) + ), + hasHTMLBlock: canInsertBlockType( + 'core/html', + getBlockRootClientId( clientId ) + ), + }; }, [ clientId ] ); const actions = []; let messageHTML; - if ( hasContent && hasHTMLBlock ) { + + const convertToHtmlButton = ( + + ); + + if ( hasContent && ! hasFreeformBlock && ! originalName ) { + if ( hasHTMLBlock ) { + messageHTML = __( + 'It appears you are trying to use the deprecated Classic block. You can leave this block intact, convert its content to a Custom HTML block, or remove it entirely. Alternatively, you can refresh the page to use the Classic block.' + ); + actions.push( convertToHtmlButton ); + } else { + messageHTML = __( + 'It appears you are trying to use the deprecated Classic block. You can leave this block intact, or remove it entirely. Alternatively, you can refresh the page to use the Classic block.' + ); + } + } else if ( hasContent && hasHTMLBlock ) { messageHTML = sprintf( /* translators: %s: block name */ __( @@ -39,11 +63,7 @@ function MissingBlockWarning( { attributes, convertToHTML, clientId } ) { ), originalName ); - actions.push( - - ); + actions.push( convertToHtmlButton ); } else { messageHTML = sprintf( /* translators: %s: block name */ diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index b91015313f4039..96d6f16926a669 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -181,6 +181,10 @@ export default function NavigationLinkEdit( { const itemLabelPlaceholder = __( 'Add label…' ); const ref = useRef(); + // Change the label using inspector causes rich text to change focus on firefox. + // This is a workaround to keep the focus on the label field when label filed is focused we don't render the rich text. + const [ isLabelFieldFocused, setIsLabelFieldFocused ] = useState( false ); + const { innerBlocks, isAtMaxNesting, @@ -424,6 +428,8 @@ export default function NavigationLinkEdit( { } } label={ __( 'Label' ) } autoComplete="off" + onFocus={ () => setIsLabelFieldFocused( true ) } + onBlur={ () => setIsLabelFieldFocused( false ) } /> ) : ( <> - { ! isInvalid && ! isDraft && ( - <> - - setAttributes( { - label: labelValue, - } ) - } - onMerge={ mergeBlocks } - onReplace={ onReplace } - __unstableOnSplitAtEnd={ () => - insertBlocksAfter( - createBlock( - 'core/navigation-link' + { ! isInvalid && + ! isDraft && + ! isLabelFieldFocused && ( + <> + + setAttributes( { + label: labelValue, + } ) + } + onMerge={ mergeBlocks } + onReplace={ onReplace } + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( + createBlock( + 'core/navigation-link' + ) ) - ) - } - aria-label={ __( - 'Navigation link text' - ) } - placeholder={ itemLabelPlaceholder } - withoutInteractiveFormatting - allowedFormats={ [ - 'core/bold', - 'core/italic', - 'core/image', - 'core/strikethrough', - ] } - onClick={ () => { - if ( ! url ) { - setIsLinkOpen( true ); } - } } - /> - { description && ( - - { description } - - ) } - - ) } - { ( isInvalid || isDraft ) && ( + aria-label={ __( + 'Navigation link text' + ) } + placeholder={ itemLabelPlaceholder } + withoutInteractiveFormatting + allowedFormats={ [ + 'core/bold', + 'core/italic', + 'core/image', + 'core/strikethrough', + ] } + onClick={ () => { + if ( ! url ) { + setIsLinkOpen( true ); + } + } } + /> + { description && ( + + { description } + + ) } + + ) } + { ( isInvalid || + isDraft || + isLabelFieldFocused ) && (
      diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index 7ec990d5b7389c..cc83463676d958 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -38,6 +38,7 @@ import { __experimentalToggleGroupControlOption as ToggleGroupControlOption, Button, Spinner, + Notice, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; @@ -485,6 +486,21 @@ function Navigation( { { open: overlayMenuPreview } ); + const submenuAccessibilityNotice = + ! showSubmenuIcon && ! openSubmenusOnClick + ? __( + 'The current menu options offer reduced accessibility for users and are not recommended. Enabling either "Open on Click" or "Show arrow" offers enhanced accessibility by allowing keyboard users to browse submenus selectively.' + ) + : ''; + + const isFirstRender = useRef( true ); // Don't speak on first render. + useEffect( () => { + if ( ! isFirstRender.current && submenuAccessibilityNotice ) { + speak( submenuAccessibilityNotice ); + } + isFirstRender.current = false; + }, [ submenuAccessibilityNotice ] ); + const colorGradientSettings = useMultipleOriginColorsAndGradients(); const stylingInspectorControls = ( <> @@ -578,6 +594,18 @@ function Navigation( { disabled={ attributes.openSubmenusOnClick } label={ __( 'Show arrow' ) } /> + + { submenuAccessibilityNotice && ( +
      + + { submenuAccessibilityNotice } + +
      + ) } ) } diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index e86561e8f4dbd0..dbf6b8c0f5ed26 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -103,7 +103,7 @@ function block_core_navigation_add_directives_to_submenu( $w, $block_attributes ) ) { // Add directives to the parent `
    2. `. $w->set_attribute( 'data-wp-interactive', true ); - $w->set_attribute( 'data-wp-context', '{ "core": { "navigation": { "isMenuOpen": { "click": false, "hover": false }, "overlay": false } } }' ); + $w->set_attribute( 'data-wp-context', '{ "core": { "navigation": { "submenuOpenedBy": {}, "type": "submenu" } } }' ); $w->set_attribute( 'data-wp-effect', 'effects.core.navigation.initMenu' ); $w->set_attribute( 'data-wp-on--focusout', 'actions.core.navigation.handleMenuFocusout' ); $w->set_attribute( 'data-wp-on--keydown', 'actions.core.navigation.handleMenuKeydown' ); @@ -123,6 +123,16 @@ function block_core_navigation_add_directives_to_submenu( $w, $block_attributes $w->set_attribute( 'data-wp-bind--aria-expanded', 'selectors.core.navigation.isMenuOpen' ); }; + // Add directives to the submenu. + if ( $w->next_tag( + array( + 'tag_name' => 'UL', + 'class_name' => 'wp-block-navigation__submenu-container', + ) + ) ) { + $w->set_attribute( 'data-wp-on--focusin', 'actions.core.navigation.openMenuOnFocus' ); + } + // Iterate through subitems if exist. block_core_navigation_add_directives_to_submenu( $w, $block_attributes ); } @@ -711,7 +721,7 @@ function render_block_core_navigation( $attributes, $content, $block ) { if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN && $should_load_view_script ) { $nav_element_directives = ' data-wp-interactive - data-wp-context=\'{ "core": { "navigation": { "isMenuOpen": { "click": false, "hover": false }, "overlay": true, "roleAttribute": "" } } }\' + data-wp-context=\'{ "core": { "navigation": { "overlayOpenedBy": {}, "type": "overlay", "roleAttribute": "" } } }\' '; $open_button_directives = ' data-wp-on--click="actions.core.navigation.openMenuOnClick" @@ -736,11 +746,11 @@ function render_block_core_navigation( $attributes, $content, $block ) { } $responsive_container_markup = sprintf( - ' + '
      -
      +
      - +
      %2$s
      diff --git a/packages/block-library/src/navigation/view.js b/packages/block-library/src/navigation/view.js index c9e0a78d6a2a3e..b0d39ef3ca4d57 100644 --- a/packages/block-library/src/navigation/view.js +++ b/packages/block-library/src/navigation/view.js @@ -1,35 +1,33 @@ /** * WordPress dependencies */ -import { store } from '@wordpress/interactivity'; +import { store as wpStore } from '@wordpress/interactivity'; const focusableSelectors = [ 'a[href]', - 'area[href]', 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', 'select:not([disabled]):not([aria-hidden])', 'textarea:not([disabled]):not([aria-hidden])', 'button:not([disabled]):not([aria-hidden])', - 'iframe', - 'object', - 'embed', '[contenteditable]', '[tabindex]:not([tabindex^="-"])', ]; -const openMenu = ( { context, ref }, menuOpenedOn ) => { - context.core.navigation.isMenuOpen[ menuOpenedOn ] = true; +const openMenu = ( store, menuOpenedOn ) => { + const { context, ref, selectors } = store; + selectors.core.navigation.menuOpenedBy( store )[ menuOpenedOn ] = true; context.core.navigation.previousFocus = ref; - if ( context.core.navigation.overlay ) { + if ( context.core.navigation.type === 'overlay' ) { // Add a `has-modal-open` class to the root. document.documentElement.classList.add( 'has-modal-open' ); } }; -const closeMenu = ( { context, selectors }, menuClosedOn ) => { - context.core.navigation.isMenuOpen[ menuClosedOn ] = false; +const closeMenu = ( store, menuClosedOn ) => { + const { context, selectors } = store; + selectors.core.navigation.menuOpenedBy( store )[ menuClosedOn ] = false; // Check if the menu is still open or not. - if ( ! selectors.core.navigation.isMenuOpen( { context } ) ) { + if ( ! selectors.core.navigation.isMenuOpen( store ) ) { if ( context.core.navigation.modal?.contains( window.document.activeElement @@ -39,18 +37,19 @@ const closeMenu = ( { context, selectors }, menuClosedOn ) => { } context.core.navigation.modal = null; context.core.navigation.previousFocus = null; - if ( context.core.navigation.overlay ) { + if ( context.core.navigation.type === 'overlay' ) { document.documentElement.classList.remove( 'has-modal-open' ); } } }; -store( { +wpStore( { effects: { core: { navigation: { - initMenu: ( { context, selectors, ref } ) => { - if ( selectors.core.navigation.isMenuOpen( { context } ) ) { + initMenu: ( store ) => { + const { context, selectors, ref } = store; + if ( selectors.core.navigation.isMenuOpen( store ) ) { const focusableElements = ref.querySelectorAll( focusableSelectors ); context.core.navigation.modal = ref; @@ -60,8 +59,9 @@ store( { focusableElements[ focusableElements.length - 1 ]; } }, - focusFirstElement: ( { context, selectors, ref } ) => { - if ( selectors.core.navigation.isMenuOpen( { context } ) ) { + focusFirstElement: ( store ) => { + const { selectors, ref } = store; + if ( selectors.core.navigation.isMenuOpen( store ) ) { ref.querySelector( '.wp-block-navigation-item > *:first-child' ).focus(); @@ -73,60 +73,87 @@ store( { selectors: { core: { navigation: { - roleAttribute: ( { context, selectors } ) => - context.core.navigation.overlay && - selectors.core.navigation.isMenuOpen( { context } ) + roleAttribute: ( store ) => { + const { context, selectors } = store; + return context.core.navigation.type === 'overlay' && + selectors.core.navigation.isMenuOpen( store ) ? 'dialog' - : '', + : ''; + }, isMenuOpen: ( { context } ) => - // The menu is opened if either `click` or `hover` is true. - Object.values( context.core.navigation.isMenuOpen ).filter( - Boolean - ).length > 0, + // The menu is opened if either `click`, `hover` or `focus` is true. + Object.values( + context.core.navigation[ + context.core.navigation.type === 'overlay' + ? 'overlayOpenedBy' + : 'submenuOpenedBy' + ] + ).filter( Boolean ).length > 0, + menuOpenedBy: ( { context } ) => + context.core.navigation[ + context.core.navigation.type === 'overlay' + ? 'overlayOpenedBy' + : 'submenuOpenedBy' + ], }, }, }, actions: { core: { navigation: { - openMenuOnHover( args ) { - openMenu( args, 'hover' ); + openMenuOnHover( store ) { + const { navigation } = store.context.core; + if ( + navigation.type === 'submenu' && + // Only open on hover if the overlay is closed. + Object.values( + navigation.overlayOpenedBy || {} + ).filter( Boolean ).length === 0 + ) + openMenu( store, 'hover' ); }, - closeMenuOnHover( args ) { - closeMenu( args, 'hover' ); + closeMenuOnHover( store ) { + closeMenu( store, 'hover' ); }, - openMenuOnClick( args ) { - openMenu( args, 'click' ); + openMenuOnClick( store ) { + openMenu( store, 'click' ); }, - closeMenuOnClick( args ) { - closeMenu( args, 'click' ); + closeMenuOnClick( store ) { + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); }, - toggleMenuOnClick: ( args ) => { - const { context } = args; - if ( context.core.navigation.isMenuOpen.click ) { - closeMenu( args, 'click' ); + openMenuOnFocus( store ) { + openMenu( store, 'focus' ); + }, + toggleMenuOnClick: ( store ) => { + const { selectors } = store; + const menuOpenedBy = + selectors.core.navigation.menuOpenedBy( store ); + if ( menuOpenedBy.click || menuOpenedBy.focus ) { + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); } else { - openMenu( args, 'click' ); + openMenu( store, 'click' ); } }, - handleMenuKeydown: ( args ) => { - const { context, event } = args; - if ( context.core.navigation.isMenuOpen.click ) { - // If Escape close the menu - if ( - event?.key === 'Escape' || - event?.keyCode === 27 - ) { - closeMenu( args, 'click' ); + handleMenuKeydown: ( store ) => { + const { context, selectors, event } = store; + if ( + selectors.core.navigation.menuOpenedBy( store ).click + ) { + // If Escape close the menu. + if ( event?.key === 'Escape' ) { + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); return; } - // Trap focus if it is an overlay (main menu) + // Trap focus if it is an overlay (main menu). if ( - context.core.navigation.overlay && - ( event.key === 'Tab' || event.keyCode === 9 ) + context.core.navigation.type === 'overlay' && + event.key === 'Tab' ) { - // If shift + tab it change the direction + // If shift + tab it change the direction. if ( event.shiftKey && window.document.activeElement === @@ -146,21 +173,21 @@ store( { } } }, - handleMenuFocusout: ( args ) => { - const { context, event } = args; + handleMenuFocusout: ( store ) => { + const { context, event } = store; // If focus is outside modal, and in the document, close menu // event.target === The element losing focus // event.relatedTarget === The element receiving focus (if any) // When focusout is outsite the document, - // `window.document.activeElement` doesn't change + // `window.document.activeElement` doesn't change. if ( - context.core.navigation.isMenuOpen.click && ! context.core.navigation.modal?.contains( event.relatedTarget ) && event.target !== window.document.activeElement ) { - closeMenu( args, 'click' ); + closeMenu( store, 'click' ); + closeMenu( store, 'focus' ); } }, }, diff --git a/packages/block-library/src/page-list/convert-to-links-modal.js b/packages/block-library/src/page-list/convert-to-links-modal.js index b212fccb774177..cd4049fecff588 100644 --- a/packages/block-library/src/page-list/convert-to-links-modal.js +++ b/packages/block-library/src/page-list/convert-to-links-modal.js @@ -5,14 +5,14 @@ import { Button, Modal } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; export const convertDescription = __( - 'This menu is automatically kept in sync with pages on your site. You can manage the menu yourself by clicking "Edit" below.' + 'This page list is synced with the published pages on your site. Detach the page list to add, delete, or reorder pages yourself.' ); export function ConvertToLinksModal( { onClick, onClose, disabled } ) { return ( - { __( 'Edit' ) } + { __( 'Detach' ) }
      diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js index 417ebed5b5e242..e4c28a22111c61 100644 --- a/packages/block-library/src/page-list/edit.js +++ b/packages/block-library/src/page-list/edit.js @@ -169,12 +169,6 @@ export default function PageListEdit( { }, new Map() ); }, [ pages ] ); - const convertToNavigationLinks = useConvertToNavigationLinks( { - clientId, - pages, - parentPageID, - } ); - const blockProps = useBlockProps( { className: classnames( 'wp-block-page-list', { 'has-text-color': !! context.textColor, @@ -189,68 +183,71 @@ export default function PageListEdit( { style: { ...context.style?.color }, } ); - const getBlockList = ( parentId = parentPageID ) => { - const childPages = pagesByParentId.get( parentId ); + const pagesTree = useMemo( + function makePagesTree( parentId = 0, level = 0 ) { + const childPages = pagesByParentId.get( parentId ); - if ( ! childPages?.length ) { - return []; - } - - return childPages.reduce( ( template, page ) => { - const hasChildren = pagesByParentId.has( page.id ); - const pageProps = { - id: page.id, - label: - // translators: displayed when a page has an empty title. - page.title?.rendered?.trim() !== '' - ? page.title?.rendered - : __( '(no title)' ), - title: page.title?.rendered, - link: page.url, - hasChildren, - }; - let item = null; - const children = getBlockList( page.id ); - item = createBlock( 'core/page-list-item', pageProps, children ); - template.push( item ); - - return template; - }, [] ); - }; + if ( ! childPages?.length ) { + return []; + } - const makePagesTree = ( parentId = 0, level = 0 ) => { - const childPages = pagesByParentId.get( parentId ); + return childPages.reduce( ( tree, page ) => { + const hasChildren = pagesByParentId.has( page.id ); + const item = { + value: page.id, + label: '— '.repeat( level ) + page.title.rendered, + rawName: page.title.rendered, + }; + tree.push( item ); + if ( hasChildren ) { + tree.push( ...makePagesTree( page.id, level + 1 ) ); + } + return tree; + }, [] ); + }, + [ pagesByParentId ] + ); - if ( ! childPages?.length ) { - return []; - } + const blockList = useMemo( + function getBlockList( parentId = parentPageID ) { + const childPages = pagesByParentId.get( parentId ); - return childPages.reduce( ( tree, page ) => { - const hasChildren = pagesByParentId.has( page.id ); - const item = { - value: page.id, - label: '— '.repeat( level ) + page.title.rendered, - rawName: page.title.rendered, - }; - tree.push( item ); - if ( hasChildren ) { - tree.push( ...makePagesTree( page.id, level + 1 ) ); + if ( ! childPages?.length ) { + return []; } - return tree; - }, [] ); - }; - const pagesTree = useMemo( makePagesTree, [ pagesByParentId ] ); - - const blockList = useMemo( getBlockList, [ - pagesByParentId, - parentPageID, - ] ); + return childPages.reduce( ( template, page ) => { + const hasChildren = pagesByParentId.has( page.id ); + const pageProps = { + id: page.id, + label: + // translators: displayed when a page has an empty title. + page.title?.rendered?.trim() !== '' + ? page.title?.rendered + : __( '(no title)' ), + title: page.title?.rendered, + link: page.url, + hasChildren, + }; + let item = null; + const children = getBlockList( page.id ); + item = createBlock( + 'core/page-list-item', + pageProps, + children + ); + template.push( item ); + + return template; + }, [] ); + }, + [ pagesByParentId, parentPageID ] + ); const { isNested, hasSelectedChild, - parentBlock, + parentClientId, hasDraggedChild, isChildOfNavigation, } = useSelect( @@ -258,7 +255,6 @@ export default function PageListEdit( { const { getBlockParentsByBlockName, hasSelectedInnerBlock, - getBlockRootClientId, hasDraggedInnerBlock, } = select( blockEditorStore ); const blockParents = getBlockParentsByBlockName( @@ -276,12 +272,19 @@ export default function PageListEdit( { isChildOfNavigation: navigationBlockParents.length > 0, hasSelectedChild: hasSelectedInnerBlock( clientId, true ), hasDraggedChild: hasDraggedInnerBlock( clientId, true ), - parentBlock: getBlockRootClientId( clientId ), + parentClientId: navigationBlockParents[ 0 ], }; }, [ clientId ] ); + const convertToNavigationLinks = useConvertToNavigationLinks( { + clientId, + pages, + parentClientId, + parentPageID, + } ); + const innerBlocksProps = useInnerBlocksProps( blockProps, { allowedBlocks: [ 'core/page-list-item' ], renderAppender: false, @@ -297,12 +300,12 @@ export default function PageListEdit( { useEffect( () => { if ( hasSelectedChild || hasDraggedChild ) { openModal(); - selectBlock( parentBlock ); + selectBlock( parentClientId ); } }, [ hasSelectedChild, hasDraggedChild, - parentBlock, + parentClientId, selectBlock, openModal, ] ); diff --git a/packages/block-library/src/page-list/use-convert-to-navigation-links.js b/packages/block-library/src/page-list/use-convert-to-navigation-links.js index 5c62342fe77cd7..4cbc69d6e6de66 100644 --- a/packages/block-library/src/page-list/use-convert-to-navigation-links.js +++ b/packages/block-library/src/page-list/use-convert-to-navigation-links.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { createBlock } from '@wordpress/blocks'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useDispatch } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; /** @@ -116,28 +116,11 @@ export function convertToNavigationLinks( pages = [], parentPageID = null ) { export function useConvertToNavigationLinks( { clientId, pages, + parentClientId, parentPageID, } ) { const { replaceBlock, selectBlock } = useDispatch( blockEditorStore ); - const { parentNavBlockClientId } = useSelect( - ( select ) => { - const { getSelectedBlockClientId, getBlockParentsByBlockName } = - select( blockEditorStore ); - - const _selectedBlockClientId = getSelectedBlockClientId(); - - return { - parentNavBlockClientId: getBlockParentsByBlockName( - _selectedBlockClientId, - 'core/navigation', - true - )[ 0 ], - }; - }, - [ clientId ] - ); - return () => { const navigationLinks = convertToNavigationLinks( pages, parentPageID ); @@ -145,6 +128,6 @@ export function useConvertToNavigationLinks( { replaceBlock( clientId, navigationLinks ); // Select the Navigation block to reveal the changes. - selectBlock( parentNavBlockClientId ); + selectBlock( parentClientId ); }; } diff --git a/packages/block-library/src/post-template/index.php b/packages/block-library/src/post-template/index.php index 3c023c80ed263d..b1499d845f39a6 100644 --- a/packages/block-library/src/post-template/index.php +++ b/packages/block-library/src/post-template/index.php @@ -97,11 +97,13 @@ function render_block_core_post_template( $attributes, $content, $block ) { $context['postId'] = $post_id; return $context; }; - add_filter( 'render_block_context', $filter_block_context ); + + // Use an early priority to so that other 'render_block_context' filters have access to the values. + add_filter( 'render_block_context', $filter_block_context, 1 ); // Render the inner blocks of the Post Template block with `dynamic` set to `false` to prevent calling // `render_callback` and ensure that no wrapper markup is included. $block_content = ( new WP_Block( $block_instance ) )->render( array( 'dynamic' => false ) ); - remove_filter( 'render_block_context', $filter_block_context ); + remove_filter( 'render_block_context', $filter_block_context, 1 ); // Wrap the render inner blocks in a `li` element with the appropriate post classes. $post_classes = implode( ' ', get_post_class( 'wp-block-post' ) ); diff --git a/packages/block-library/src/post-time-to-read/block.json b/packages/block-library/src/post-time-to-read/block.json index 8f88db46d51aef..281e9bb1f1b210 100644 --- a/packages/block-library/src/post-time-to-read/block.json +++ b/packages/block-library/src/post-time-to-read/block.json @@ -24,7 +24,11 @@ "html": false, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/post-title/index.php b/packages/block-library/src/post-title/index.php index e1d4b255c57733..1769b199cebf17 100644 --- a/packages/block-library/src/post-title/index.php +++ b/packages/block-library/src/post-title/index.php @@ -19,8 +19,11 @@ function render_block_core_post_title( $attributes, $content, $block ) { return ''; } - $post = get_post( $block->context['postId'] ); - $title = get_the_title( $post ); + /** + * The `$post` argument is intentionally omitted so that changes are reflected when previewing a post. + * See: https://github.com/WordPress/gutenberg/pull/37622#issuecomment-1000932816. + */ + $title = get_the_title(); if ( ! $title ) { return ''; @@ -33,7 +36,7 @@ function render_block_core_post_title( $attributes, $content, $block ) { if ( isset( $attributes['isLink'] ) && $attributes['isLink'] ) { $rel = ! empty( $attributes['rel'] ) ? 'rel="' . esc_attr( $attributes['rel'] ) . '"' : ''; - $title = sprintf( '%4$s', get_the_permalink( $post ), esc_attr( $attributes['linkTarget'] ), $rel, $title ); + $title = sprintf( '%4$s', get_the_permalink( $block->context['postId'] ), esc_attr( $attributes['linkTarget'] ), $rel, $title ); } $classes = array(); diff --git a/packages/block-library/src/query-pagination/edit.js b/packages/block-library/src/query-pagination/edit.js index 8ab1f63377949b..7598eba5c1cacf 100644 --- a/packages/block-library/src/query-pagination/edit.js +++ b/packages/block-library/src/query-pagination/edit.js @@ -34,20 +34,23 @@ export default function QueryPaginationEdit( { setAttributes, clientId, } ) { - const hasNextPreviousBlocks = useSelect( ( select ) => { - const { getBlocks } = select( blockEditorStore ); - const innerBlocks = getBlocks( clientId ); - /** - * Show the `paginationArrow` and `showLabel` controls only if a - * `QueryPaginationNext/Previous` block exists. - */ - return innerBlocks?.find( ( innerBlock ) => { - return [ - 'core/query-pagination-next', - 'core/query-pagination-previous', - ].includes( innerBlock.name ); - } ); - }, [] ); + const hasNextPreviousBlocks = useSelect( + ( select ) => { + const { getBlocks } = select( blockEditorStore ); + const innerBlocks = getBlocks( clientId ); + /** + * Show the `paginationArrow` and `showLabel` controls only if a + * `QueryPaginationNext/Previous` block exists. + */ + return innerBlocks?.find( ( innerBlock ) => { + return [ + 'core/query-pagination-next', + 'core/query-pagination-previous', + ].includes( innerBlock.name ); + } ); + }, + [ clientId ] + ); const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps( blockProps, { template: TEMPLATE, diff --git a/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap index 5b5df918f2beeb..65d87d5b0d7bd4 100644 --- a/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap +++ b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap @@ -22,6 +22,16 @@ exports[`Quote block transforms to Group block 1`] = ` " `; +exports[`Quote block transforms to Paragraph block 1`] = ` +" +

      "This will make running your own blog a viable alternative again."

      + + + +

      Adrian Zumbrunnen

      +" +`; + exports[`Quote block transforms to Pullquote block 1`] = ` "

      "This will make running your own blog a viable alternative again."

      Adrian Zumbrunnen
      diff --git a/packages/block-library/src/quote/test/transforms.native.js b/packages/block-library/src/quote/test/transforms.native.js index 46c4eb2b6f9727..25030e0a018d41 100644 --- a/packages/block-library/src/quote/test/transforms.native.js +++ b/packages/block-library/src/quote/test/transforms.native.js @@ -21,7 +21,11 @@ const initialHtml = ` `; const transformsWithInnerBlocks = [ 'Columns', 'Group' ]; -const blockTransforms = [ 'Pullquote', ...transformsWithInnerBlocks ]; +const blockTransforms = [ + 'Pullquote', + 'Paragraph', + ...transformsWithInnerBlocks, +]; setupCoreBlocks(); diff --git a/packages/block-library/src/quote/transforms.js b/packages/block-library/src/quote/transforms.js index d4cd77177bf030..4e153a6399029f 100644 --- a/packages/block-library/src/quote/transforms.js +++ b/packages/block-library/src/quote/transforms.js @@ -109,6 +109,19 @@ const transforms = { } ); }, }, + { + type: 'block', + blocks: [ 'core/paragraph' ], + transform: ( { citation }, innerBlocks ) => + citation + ? [ + ...innerBlocks, + createBlock( 'core/paragraph', { + content: citation, + } ), + ] + : innerBlocks, + }, { type: 'block', blocks: [ 'core/group' ], diff --git a/packages/block-library/src/site-logo/block.json b/packages/block-library/src/site-logo/block.json index 747f1ab94a9c3b..d1e3d1b20c3dad 100644 --- a/packages/block-library/src/site-logo/block.json +++ b/packages/block-library/src/site-logo/block.json @@ -40,7 +40,11 @@ }, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } } }, "styles": [ diff --git a/packages/block-library/src/site-tagline/block.json b/packages/block-library/src/site-tagline/block.json index 7382c084ee6426..22fb59aab5ead7 100644 --- a/packages/block-library/src/site-tagline/block.json +++ b/packages/block-library/src/site-tagline/block.json @@ -25,7 +25,11 @@ }, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/site-title/block.json b/packages/block-library/src/site-title/block.json index 2c37258acb3ebb..e936bad0e45152 100644 --- a/packages/block-library/src/site-title/block.json +++ b/packages/block-library/src/site-title/block.json @@ -40,7 +40,11 @@ }, "spacing": { "padding": true, - "margin": true + "margin": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/social-links/block.json b/packages/block-library/src/social-links/block.json index 064b077b5e9ed9..20206511a4c96c 100644 --- a/packages/block-library/src/social-links/block.json +++ b/packages/block-library/src/social-links/block.json @@ -73,7 +73,9 @@ "padding": true, "units": [ "px", "em", "rem", "vh", "vw" ], "__experimentalDefaultControls": { - "blockGap": true + "blockGap": true, + "margin": true, + "padding": false } } }, diff --git a/packages/block-library/src/table/block.json b/packages/block-library/src/table/block.json index 54e4bc26cac7d1..d1139d6c55addf 100644 --- a/packages/block-library/src/table/block.json +++ b/packages/block-library/src/table/block.json @@ -166,7 +166,11 @@ }, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "typography": { "fontSize": true, diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index b7ae4f6043afb1..a4d2e5f2a017e2 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -77,7 +77,7 @@ export default function TemplatePartEdit( { area: _area, }; }, - [ templatePartId, clientId ] + [ templatePartId, attributes.area, clientId ] ); const { templateParts } = useAlternativeTemplateParts( area, diff --git a/packages/block-library/src/verse/block.json b/packages/block-library/src/verse/block.json index 5fabf49adec7be..d0fffc8ae50769 100644 --- a/packages/block-library/src/verse/block.json +++ b/packages/block-library/src/verse/block.json @@ -46,7 +46,11 @@ }, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } }, "__experimentalBorder": { "radius": true, diff --git a/packages/block-library/src/video/block.json b/packages/block-library/src/video/block.json index df2d2075375ff8..debe6f20fe53f7 100644 --- a/packages/block-library/src/video/block.json +++ b/packages/block-library/src/video/block.json @@ -83,7 +83,11 @@ "align": true, "spacing": { "margin": true, - "padding": true + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } } }, "editorStyle": "wp-block-video-editor", diff --git a/packages/block-serialization-default-parser/CHANGELOG.md b/packages/block-serialization-default-parser/CHANGELOG.md index 6a1e83fff5ab39..d3ca0243114f9c 100644 --- a/packages/block-serialization-default-parser/CHANGELOG.md +++ b/packages/block-serialization-default-parser/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.37.0 (2023-07-05) + ## 4.36.0 (2023-06-23) ## 4.35.0 (2023-06-07) diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index b170b16f02770b..a0b8414ded1e7f 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-default-parser", - "version": "4.36.0", + "version": "4.37.0", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/block-serialization-spec-parser/CHANGELOG.md b/packages/block-serialization-spec-parser/CHANGELOG.md index 944bb874da02b3..8f3d032be39c62 100644 --- a/packages/block-serialization-spec-parser/CHANGELOG.md +++ b/packages/block-serialization-spec-parser/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.37.0 (2023-07-05) + ## 4.36.0 (2023-06-23) ## 4.35.0 (2023-06-07) diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 8c8653198d3484..db2592278c16c7 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-spec-parser", - "version": "4.36.0", + "version": "4.37.0", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index f26e897aebbd07..249b254a92fa40 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 12.14.0 (2023-07-05) + ## 12.13.0 (2023-06-23) ## 12.12.0 (2023-06-07) diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 582774a9d6a1c0..89ed8b687f447b 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "12.13.0", + "version": "12.14.0", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index f601a3f59314fe..bf866b7a2143ba 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -129,7 +129,7 @@ import { store as blocksStore } from '../store'; * then no preview is shown. */ -export const serverSideBlockDefinitions = {}; +const serverSideBlockDefinitions = {}; function isObject( object ) { return object !== null && typeof object === 'object'; diff --git a/packages/blocks/src/api/test/registration.js b/packages/blocks/src/api/test/registration.js index ff75590b40007d..42f4dcfbf0e48f 100644 --- a/packages/blocks/src/api/test/registration.js +++ b/packages/blocks/src/api/test/registration.js @@ -28,7 +28,6 @@ import { getBlockSupport, hasBlockSupport, isReusableBlock, - serverSideBlockDefinitions, unstable__bootstrapServerSideBlockDefinitions, // eslint-disable-line camelcase } from '../registration'; import { BLOCK_ICON_DEFAULT, DEPRECATED_ENTRY_KEYS } from '../constants'; @@ -822,7 +821,6 @@ describe( 'blocks', () => { styles: [], variations: [], save: () => null, - ...serverSideBlockDefinitions[ name ], ...blockSettingsWithDeprecations, }, DEPRECATED_ENTRY_KEYS diff --git a/packages/browserslist-config/CHANGELOG.md b/packages/browserslist-config/CHANGELOG.md index cf43f3751f74ba..ce9ff2903239b1 100644 --- a/packages/browserslist-config/CHANGELOG.md +++ b/packages/browserslist-config/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.20.0 (2023-07-05) + ## 5.19.0 (2023-06-23) ## 5.18.0 (2023-06-07) diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index d90ecf77f0ce0a..be81cad67529ff 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/browserslist-config", - "version": "5.19.0", + "version": "5.20.0", "description": "WordPress Browserslist shared configuration.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/commands/CHANGELOG.md b/packages/commands/CHANGELOG.md index e76f27c9d787ba..1645fa29f2b44e 100644 --- a/packages/commands/CHANGELOG.md +++ b/packages/commands/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.8.0 (2023-07-05) + ## 0.7.0 (2023-06-23) ## 0.6.0 (2023-06-07) diff --git a/packages/commands/README.md b/packages/commands/README.md index 130812664368ff..c6504ff638cdea 100644 --- a/packages/commands/README.md +++ b/packages/commands/README.md @@ -38,7 +38,7 @@ _Type_ ### useCommand -Attach a command to the Global command menu. +Attach a command to the command palette. _Parameters_ @@ -46,7 +46,7 @@ _Parameters_ ### useCommandLoader -Attach a command loader to the Global command menu. +Attach a command loader to the command palette. _Parameters_ diff --git a/packages/commands/package.json b/packages/commands/package.json index 92d7cd61bd16c4..8b08f115820479 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/commands", - "version": "0.7.0", + "version": "0.8.0", "description": "Handles the commands menu.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/commands/src/components/command-menu.js b/packages/commands/src/components/command-menu.js index 9f59db3f6f53cb..b4a828f34303db 100644 --- a/packages/commands/src/components/command-menu.js +++ b/packages/commands/src/components/command-menu.js @@ -155,7 +155,7 @@ export function CommandMenu() { registerShortcut( { name: 'core/commands', category: 'global', - description: __( 'Open the global command menu' ), + description: __( 'Open the command palette' ), keyCombination: { modifier: 'primary', character: 'k', @@ -192,7 +192,7 @@ export function CommandMenu() { }; useEffect( () => { - // Focus the command menu input when mounting the modal. + // Focus the command palette input when mounting the modal. if ( isOpen ) { commandMenuInput.current.focus(); } @@ -211,7 +211,7 @@ export function CommandMenu() { __experimentalHideHeader >
      - +